MyBatis

在使用MyBatis进行数据库操作时,中间表关联查询是一种常见的需求,尤其是在处理多表关联关系时。以下是一个基于MyBatis实现中间表关联查询的详细步骤和示例,假设我们有一个典型的多对多关系:学生表(Student)课程表(Course)和中间表学生课程表(StudentCourse)

1. 数据库表结构设计

学生表(Student)

sql复制

CREATE TABLE Student (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age INT
);
课程表(Course)

sql复制

CREATE TABLE Course (
    id INT PRIMARY KEY AUTO_INCREMENT,
    course_name VARCHAR(50)
);
中间表(StudentCourse)

sql复制

CREATE TABLE StudentCourse (
    student_id INT,
    course_id INT,
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES Student(id),
    FOREIGN KEY (course_id) REFERENCES Course(id)
);

2. MyBatis配置

数据库连接配置(db.properties

properties复制

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=your_password
MyBatis配置文件(mybatis-config.xml

xml复制

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="StudentMapper.xml"/>
        <mapper resource="CourseMapper.xml"/>
    </mappers>
</configuration>

3. 实体类设计

Student类

java复制

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private List<Course> courses; // 学生所选课程

    // Getters and Setters
}
Course类

java复制

public class Course {
    private Integer id;
    private String courseName;
    private List<Student> students; // 课程所选学生

    // Getters and Setters
}

4. Mapper接口和SQL映射文件

StudentMapper接口

java复制

import java.util.List;

public interface StudentMapper {
    List<Student> getStudentsWithCourses();
}
StudentMapper.xml

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="com.example.mapper.StudentMapper">
    <resultMap id="StudentResultMap" type="Student">
        <id property="id" column="student_id"/>
        <result property="name" column="student_name"/>
        <result property="age" column="student_age"/>
        <collection property="courses" ofType="Course" column="course_id" select="getCourseById"/>
    </resultMap>

    <resultMap id="CourseResultMap" type="Course">
        <id property="id" column="course_id"/>
        <result property="courseName" column="course_name"/>
    </resultMap>

    <select id="getStudentsWithCourses" resultMap="StudentResultMap">
        SELECT s.id AS student_id, s.name AS student_name, s.age AS student_age, sc.course_id
        FROM Student s
        LEFT JOIN StudentCourse sc ON s.id = sc.student_id
    </select>

    <select id="getCourseById" parameterType="int" resultMap="CourseResultMap">
        SELECT id AS course_id, course_name
        FROM Course
        WHERE id = #{courseId}
    </select>
</mapper>

5. 测试代码

测试类

java复制

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.io.Resources;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

public class MyBatisTest {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            List<Student> students = studentMapper.getStudentsWithCourses();

            for (Student student : students) {
                System.out.println("Student: " + student.getName());
                for (Course course : student.getCourses()) {
                    System.out.println("  Course: " + course.getCourseName());
                }
            }
        }
    }
}

6. 运行结果

假设数据库中有以下数据:

  • 学生表:

    • ID: 1, Name: Alice, Age: 20

    • ID: 2, Name: Bob, Age: 22

  • 课程表:

    • ID: 1, CourseName: Math

    • ID: 2, CourseName: English

  • 中间表:

    • StudentID: 1, CourseID: 1

    • StudentID: 1, CourseID: 2

    • StudentID: 2, CourseID: 1

运行测试代码后,输出结果如下:

复制

Student: Alice
  Course: Math
  Course: English
Student: Bob
  Course: Math

总结

通过MyBatis的resultMap和嵌套查询,我们可以方便地实现中间表的关联查询。resultMap用于定义结果映射规则,<collection>标签用于处理多对多关系,通过select属性指定子查询来加载关联对象。

 怎么处理MySQL的慢查询?
1、开启慢查询日志,准确定位到哪个sql语句出现了问题

2、分析sql语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写

3、分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引

4、如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

优化语句:

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。

 如何理解Springboot中的Starter?
starter就是定义⼀个starter的jar包,写⼀个@Configuration配置类、将这些bean定义在⾥⾯,然后在starter包的META-INF/spring.factories中写⼊该配置类,springboot会按照约定来加载该配置类开发⼈员只需要将相应的starter包依赖进应⽤,进⾏相应的属性配置(使⽤默认配置时,不需要配置),就可以直接进⾏代码开发,使⽤对应的功能了。

⽐如mybatis-spring-boot-starter,spring-boot-starter-redis,我们自己写一个starter自动配置启动,写一个我们自己的线程池,用@Configuration+@Bean定义一个线程池,将这个配置类的路径写到spring.factories中,就可以了,打上jar包,在其他地方引用,用@Autowired自动注入我们的Bean使用就可以。

 如何实现一个IOC容器?
IOC(Inversion of Control),意思是控制反转,不是什么技术,而是一种设计思想,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

在传统的程序设计中,我们直接在对象内部通过new进行对象创建,是程序主动去创建依赖对象,而IOC是有专门的容器来进行对象的创建,即IOC容器来控制对象的创建。

在传统的应用程序中,我们是在对象中主动控制去直接获取依赖对象,这个是正转,反转是由容器来帮忙创建及注入依赖对象,在这个过程过程中,由容器帮我们查找级注入依赖对象,对象只是被动的接受依赖对象。

先准备一个基本的容器对象,包含一些map结构的集合,用来方便后续过程中存储具体的对象
进行配置文件的读取工作或者注解的解析工作,将需要创建的bean对象都封装成BeanDefinition对象存储在容器中
容器将封装好的BeanDefinition对象通过反射的方式进行实例化,完成对象的实例化工作
进行对象的初始化操作,也就是给类中的对应属性值就行设置,也就是进行依赖注入,完成整个对象的创建,变成一个完整的bean对象,存储在容器的某个map结构中
通过容器对象来获取对象,进行对象的获取和逻辑处理工作
提供销毁操作,当对象不用或者容器关闭的时候,将无用的对象进行销毁
 SpringCloud核心组件有哪些?分别有什么作用?
1)服务发现——Netflix Eurek

该系统下还分为Eureka服务端和Eureka客户端,Eureka服务端用作服务注册中心,支持集群部署。Eureka客户端是一个java客户端,用来处理服务注册与发现。

2)客服端负载均衡——Netflix Ribbon

基于Http和Tcp的客户端负载均衡,使得面向REST请求时变换为客户端的负载服务调用,提供客户端的软件负载均衡算法。

3)断路器——Netflix Hystrix

它的作用是保护系统,控制故障范围。

4)服务网关——Netflix Zuul

提供api网关,路由,负载均衡等作用

5)分布式配置——Spring Cloud Config

提供服务端和客户端,服务器存储后端的默认实现使用git

8 讲一下对AOP的理解
AOP全称叫做 Aspect Oriented Programming 面向切面编程。它是为解耦而生的,解耦是程序员编码开发过程中一直追求的境界,AOP在业务类的隔离上,绝对是做到了解耦,在这里面有几个核心的概念:

切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schemabasedapproach)或者在普通类中以 @Aspect 注解(@AspectJ 注解方式)来实现。
连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。
通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”,“before” and “after”等等。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified 接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。
目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。
AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码冗余太多,因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去,AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。

9 讲一下对IOC的理解
IOC容器:实际上就是map,里面存的是各种对象(在XML里配置的bean节点,@respository,@service,@controller,@component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里,扫描到打上上述注解的类还是通过反射的方式创建对象放到map里面。

这个时候map里面就有各种对象了,接下来我们再代码里面需要用到里面的对象时,再通过DI注入(@Autowired,@Resources,@Qualifier等注解),xml里bean 节点内的ref属性,项目启动时读取xml节点ref属性根据id注入,也会扫描到这些注解,根据类型或者id注入,id就是对象名。

控制反转:
没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须去主动创建对象B或者使用已经创建的对象B,无论是创建还是使用对象B,控制权都在A类。

引入IOC容器后,对象A和对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建对象B注入到对象A所需要的地方。

通过前后对比,不难看出:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是控制反转。

依赖注入:
“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变成了由IOC容器自动注入。依赖注入是实现IOC的方法,就是由IOC在运行期间,动态的将某种依赖关系注入到对象之中。

10 索引的设计原则有哪些?
在进行索引设计的时候,应保证索引字段占用的空间越小越好,这只是一个大的方向,还有一些细节需要注意:

1、适合索引的列是出现在where子句中的列,或者链接子句中的列。

2、基数较小的表,索引效果差,没必要创建索引。

3、在选择索引列的时候,越短越好,可以指定某些列的一部分不,没必要用全部字段的值。

4、不要给表的没有给字段都创建索引,并不是索引越多越好。

5、定义有外键的数据列一定药创建索引。

6、更新频繁的字段不要有索引。

7、创建索引的列不要过多,可以创建组合索引,但是组合索引的列的个数不建议太多。


                        
原文链接:https://blog.youkuaiyun.com/weixin_43660476/article/details/124371767

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值