Mybatis多表联合查询,嵌套查询,动态SQL

本文详细介绍了Mybatis中多表联合查询的一对一、一对多、多对多关系处理,以及嵌套查询的实现,包括Where、Set和Foreach标签的应用,帮助理解动态SQL的使用。
部署运行你感兴趣的模型镜像

Mybatis多表联合查询,嵌套查询,动态SQL

Mybatis多表联合查询

一对一

一对一查询:通过一方关联查询出另外一方的关系数据

  1. 创建数据库和相关数据表
  2. 创建需要关联查询的实体类,里面包含相关的属性
//丈夫查妻子
public class Husband {    
    private Integer id;    
    private String name;   
    private Wife wife;
}
public class Wife {

    private Integer id;
    private String name;
}
  1. 编写HusbandMapper接口,为了调用方法
public interface HusbandMapper {

    List<Husband> findByName(String name);
}
  1. 编写相关的Sql语句
    • 关联查询的时候需要用resultMap进行自定义标签的映射
    • 非关联单表查询的时候可以直接使用resultType进行查询
    • property表示属性对应的是实体类的字段名称
    • column表示的是自己定义的属性值,与sql语句定义的字段名称相同
    • associationjavaType在一对一关联查询的时候使用
    • association里映射的是被关联查询的表和属性值
   <resultMap id="husbandAndWife" type="husband">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="wife" javaType="wife">
            <id property="id" column="wid"/>
            <result property="name" column="wname"/>
        </association>
    </resultMap>
    
    <select id="findByName" parameterType="string" resultMap="husbandAndWife">
        select h.id,h.name,w.id wid, w.name wname from t_husband h,t_wife w 
        where h.id = w.id and h.name = #{name}
    </select>
  1. 编写HusbandMapperTest测试类
public class CustomerMapperTest {

    private SqlSessionFactory sqlSessionFactory;

   @Before
   public void init() throws IOException {
       //加载核心配置文件
       InputStream inputStream = Resources.getResourceAsStream("mybatis-config");
       //创建会话工厂
       sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
   }

   @Test
   public void test(){
       //打开会话工厂
       SqlSession sqlSession = sqlSessionFactory.openSession();
       //获取内容
       CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
       //遍历Customer表数据
       List<Customer> ret = mapper.findByName("aa");
       //输出
       System.out.println(ret);
   }
一对多

一个用户可以关联查询出自己的订单信息

  1. 创建数据库和相关数据表
  2. 创建需要关联查询的实体类,里面包含相关的属性
public class Customer {

    private Integer id;
    private String name;
    
    private List<Order> orders;
}
public class Order {

    private Integer id;
    private String goods;
    private Integer cid;
}
  1. 编写CustomerMapper接口,为了调用方法
public interface CustomerMapper {

    List<Customer> findByName(String name);
}
  1. 编写相关的Sql语句
    • collectionofType用于对多查询的场合
    • 左外连接,以左边表为基础和交集取值
<resultMap id="customerAndOrders" type="customer">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="orders" ofType="order">
        <id property="id" column="id"/>
        <result property="o.goods" column="o.goods"/>
        <result property="o.c_id" column="o.c_id"/>
        </collection>
    </resultMap>

    <select id="findByName" resultMap="customerAndOrders">
        select c.* ,o.id oid,o.goods,o.c_id from t_customer c 
        left join t_order o on c.id = o.c_id where c.name = #{name}
    </select>
  1. 编写CustomerMapperTest测试类
public class CustomerMapperTest {

    private SqlSessionFactory sqlSessionFactory;

   @Before
   public void init() throws IOException {
       InputStream inputStream = Resources.getResourceAsStream("mybatis-config");
       sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
   }

   @Test
   public void test(){
       SqlSession sqlSession = sqlSessionFactory.openSession();
       CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
       List<Customer> ret = mapper.findByName("aa");
       System.out.println(ret);
   }
多对多

一个老师对应多个学生,一个学生也对应多个老师

  1. 创建数据库和相关数据表
  2. 创建需要关联查询的实体类,里面包含相关的属性
public class Teacher {

    private Integer id;
    private String name;

    private List<Student> students;
}
public class Student {

    private Integer id;
    private String name;
}
  1. 编写TeacherMapper接口,为了调用方法
public interface TeacherMapper {

    List<Teacher> findByName(String name);
}
  1. 编写相关的Sql语句
    • 多对多查询需要借助一个t_s中间表来完成关联
  <resultMap id="teacherAndStudent" type="teacher">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="students" ofType="student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
        </collection>
    </resultMap>

    <select id="findByName" resultMap="teacherAndStudent">
        select t.id,t.name,s.id sid,s.name sname from t_teacher t
            left join t_s ts on t.id = ts.t_id
            left join t_student s on ts.s_id = s.id
            where t.name = #{name}
    </select>
  1. 编写TeacherMapperTest测试类
public class TeacherMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void test(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        List<Teacher> ret = mapper.findByName("唐丹");
        System.out.println(ret);
    }
}

Mybatis的嵌套查询

嵌套查询是将原来多表查询中的联合查询语句拆成单个表的查询,再使用MyBatis的语法嵌套在一起

嵌套查询使用时,先查询a表的信息,然后依赖a和b表的外键约束,利用in(),再次查询b表对应到a表上的信息。该方式可以改为饿汉式,内存使用较小,但需要多次访问数据库而导致消耗时间多。

一对一嵌套查询

查询一个订单,同时查询出该订单所属的用户

	<select id="findById" resultType="customer">
        select * from t_customer where id = #{id}
    </select>

    <resultMap id="orderAndCustomer2" type="order">
        <id property="id" column="id"/>
        <result property="goods" column="goods"/>
        <result property="cid" column="cid"/>
        <association property="customer"
                     javaType="customer"
                     column="c_id"
                     select="findById"/>
    </resultMap>

    <select id="findByGoods2" resultMap="orderAndCustomer2">
        select * from t_order where goods = #{goods}
    </select>
一对多嵌套查询

查询一个用户,与此同时查询该用户具有的订单

	<resultMap id="baseMap" type="order">
        <id property="id" column="id"/>
        <result property="goods" column="goods"/>
        <result property="cid" column="c_id"/>
    </resultMap>

    <select id="findByCid" resultMap="baseMap">
        select * from t_order where c_id = #{cid}
    </select>

    <resultMap id="customerAndOrders2" type="customer">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="orders"
                    ofType="order"
                    column="id"
                    select="findByCid"/>

    </resultMap>

    <select id="findByName2" resultMap="customerAndOrders2">
        select * from t_customer where name = #{name}
    </select>

动态Sql查询

<insert id="save" parameterType="girl" keyProperty="id" useGeneratedKeys="true">
        insert into girl values (null, #{name},#{age},#{address})
    </insert>

    <sql id="selectAll">
        select * from girl
    </sql>

    <select id="findById" parameterType="int" resultType="girl">
        <include refid="selectAll"/> where id = #{id}
    </select>

    <select id="findAll" resultType="girl">
        <include refid="selectAll"/>
    </select>
Where标签
  • ****标签相当于 where 1=1

  • 如果没有条件,就不会拼接where关键字

  • where标签可以忽略我们成立条件前面的and或者or关键字


    <select id="findByGirl" resultType="girl">
        select * from girl
        <where>
            <if test="name != null and name != ''">
                and name = #{name}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="address != null and address != ''">
                and address = #{address}
            </if>
        </where>
    </select>

    <select id="findByIds" resultType="girl">
        select * from girl
        <where>
            <foreach collection="list" open="id in (" item="id" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>
Set标签
  • set标签在更新的时候,自动加上set关键字,然后会去掉最后一个条件的逗号
 <update id="update" parameterType="girl">
        update girl
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="age != null and age != ''">
                age = #{age},
            </if>
            <if test="address != null and address != ''">
                address = #{address}
            </if>
        </set>
        where id = #{id}
	</update>

Foreach标签
  • 标签用来对数据进行循环遍历
    • collection:代表遍历的集合元素
    • open:代表语句开始的部分
    • close:代表语句结束的部分
    • item:代表遍历集合的每一个元素,生成的变量名
    • sperator:代表分隔符
 <select id="findByIds" resultType="girl">
        select * from girl
        <where>
            <foreach collection="list" open="id in (" item="id" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

MyBatis 本身是一个基于 Java 的持久层框架,它并不直接控制 SQL 的性能,而是将 SQL 的编写和执行交给开发者控制。因此,**MyBatis 联合查询的性能**,主要取决于以下几个方面: --- ## ✅ 1. SQL 编写质量 ### 优化建议: - **避免 SELECT ***:只选择需要的字段,减少数据传输量。 - **合理使用 JOIN**:尽量使用 `INNER JOIN`,避免不必要的 `LEFT JOIN` 或 `FULL JOIN`。 - **使用索引字段作为关联条件**:确保连接字段(如 `user_id`)在中建立了索引。 - **避免 N+1 查询问题**:使用 `resultMap` + `collection` 或 `association` 预加载关联数据。 ### 示例优化 SQL: ```sql SELECT u.id, u.name, o.order_no FROM user u INNER JOIN orders o ON u.id = o.user_id WHERE u.status = 'ACTIVE'; ``` --- ## ✅ 2. 数据库索引优化 - **主键和外键字段要有索引** - **对经常用于查询、排序、分组的字段建立复合索引** - **避免在 WHERE 子句中对字段进行函数操作**(会导致索引失效) --- ## ✅ 3. MyBatis 的配置和使用方式影响性能 ### 使用 `resultMap` 避免 N+1 查询(推荐) #### 示例:一对查询(用户-订单) ```xml <resultMap id="userResultMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <collection property="orders" ofType="Order"> <id property="id" column="order_id"/> <result property="orderNo" column="order_no"/> </collection> </resultMap> <select id="selectUsersWithOrders" resultMap="userResultMap"> SELECT u.id, u.name, o.id AS order_id, o.order_no FROM user u LEFT JOIN orders o ON u.id = o.user_id </select> ``` ### 优点: - 一次查询完成,避免数据库访问 - 减少网络 IO,提升性能 --- ## ✅ 4. 分页查询优化(大数据量) 如果你做的是分页查询(如 LIMIT/OFFSET),在联合时需要注意: ### 优化建议: - 使用子查询先过滤主 ID,再进行 JOIN - 避免在大数据量下使用 OFFSET 分页(会导致性能下降) #### 示例: ```sql SELECT u.id, u.name, o.order_no FROM ( SELECT id FROM user WHERE status = 'ACTIVE' LIMIT 10 OFFSET 0 ) u_sub JOIN user u ON u.id = u_sub.id LEFT JOIN orders o ON u.id = o.user_id; ``` --- ## ✅ 5. 缓存机制提升性能 MyBatis 支持一级缓存(SqlSession 级别)和二级缓存(Mapper 级别)。 ### 启用二级缓存: ```xml <cache/> ``` > 适用于读写少的联合查询结果缓存,比如报数据、字典数据等。 --- ## ✅ 6. 数据库性能瓶颈 即使 SQL 写得再好,如果数据库本身性能不足,联合查询也会变慢: ### 建议: - 使用数据库性能分析工具(如 `EXPLAIN ANALYZE`) - 监控慢查询日志 - 使用连接池(如 HikariCP)减少连接开销 --- ## ✅ 7. 实测性能对比(简单参考) | 查询方式 | 描述 | 性能 | |----------|------|------| | 单查询 | 只查一张 | ⚡ 快 | | JOIN 查询(有索引) | 联合,字段有索引 | ⚡ 快 | | JOIN 查询(无索引) | 没有索引,全扫描 | ❌ 慢 | | 次单查询(N+1) | 每个关联单独查 | ❌ 慢 | | 分页的联合查询(合理优化) | 使用子查询先分页 | ⚡ 快 | | 分页的联合查询(OFFSET 大值) | OFFSET 很大,如 100000 | ❌ 慢 | --- ## ✅ 总结 | 影响因素 | 说明 | 推荐做法 | |----------|------|----------| | SQL 编写 | 查询语句是否高效 | 使用 JOIN、避免 *、合理 WHERE | | 索引 | 是否命中索引 | 给连接字段、查询字段加索引 | | 查询结构 | 是否使用 resultMap | 使用嵌套查询或预加载 | | 分页机制 | 是否优化分页 | 使用子查询分页 | | 数据库性能 | 数据库负载、连接数等 | 监控、优化慢查询 | | 缓存 | 是否启用缓存 | 启用二级缓存提高命中率 | --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值