[Preguntas de la entrevista MyBatis (20 preguntas)]

Preguntas de la entrevista MyBatis (20 preguntas)

imagen-20230820160603581

Base

1. ¿Qué es MyBatis?

Vamos a explotarlo primero:

  • Mybatis es un marco semi-ORM (Object Relational Mapping) que encapsula JDBC internamente . Al desarrollar, solo necesita concentrarse en la declaración SQL en sí y no necesita gastar energía en procesos complejos como cargar controladores, crear conexiones y creando declaraciones. Los programadores escriben directamente SQL original, que puede controlar estrictamente el rendimiento de ejecución de SQL y proporcionar una alta flexibilidad .
  • MyBatis puede usar XML o anotaciones para configurar y mapear información nativa, mapear POJO en registros en la base de datos, evitando casi todo el código JDBC y la configuración manual de parámetros y obteniendo conjuntos de resultados.

Hablemos de las desventajas:

  • La carga de trabajo de escribir declaraciones SQL es relativamente grande, especialmente cuando hay muchos campos y tablas relacionadas, lo que requiere que los desarrolladores tengan ciertas habilidades para escribir declaraciones SQL.
  • Las declaraciones SQL dependen de la base de datos, lo que resulta en una mala portabilidad de la base de datos y la base de datos no se puede reemplazar a voluntad.

¿Qué es ORM?

imagen-20230820160803554

ORM (Object Relational Mapping) es una tecnología que resuelve la relación de mapeo entre datos de bases de datos relacionales y objetos Java simples (POJO).

En pocas palabras, ORM persiste automáticamente los objetos del programa en una base de datos relacional mediante el uso de metadatos que describen el mapeo entre el objeto y la base de datos .

¿Por qué se dice que Mybatis es una herramienta de mapeo ORM semiautomática? ¿Cuál es la diferencia entre este y completamente automático?

  • Hibernate es una herramienta de mapeo ORM completamente automática. Cuando usa Hibernate para consultar objetos relacionados u objetos de colección relacionados, puede obtenerlos directamente según el modelo de relación de objetos, por lo que es completamente automático.
  • Cuando Mybatis consulta objetos relacionados u objetos de colección relacionados, es necesario escribir SQL manualmente para completarlo, por lo que se denomina herramienta de mapeo ORM semiautomática.

¿Cuáles son las deficiencias de la programación JDBC y cómo las resuelve MyBatis?

imagen-20230820160939128

  1. La creación y liberación frecuente de conexiones de datos provoca un desperdicio de recursos del sistema y afecta el rendimiento del sistema. Configure el grupo de conexiones de datos en mybatis-config.xml y utilice el grupo de conexiones para administrar de manera uniforme las conexiones de la base de datos.
  2. Escribir declaraciones SQL en el código hace que el código sea difícil de mantener . Configure las declaraciones SQL en el archivo XXXXmapper.xml y sepárelas del código Java.
  3. Pasar parámetros a la declaración SQL es problemático , porque la condición donde la declaración SQL no es necesariamente cierta, puede ser más o menos, y el marcador de posición debe corresponder al parámetro. Mybatis asigna automáticamente objetos java a declaraciones sql.
  4. Es problemático analizar el conjunto de resultados . Los cambios de SQL provocarán cambios en el código de análisis y es necesario recorrerlo antes del análisis. Sería más conveniente analizar los registros de la base de datos en objetos pojo. Mybatis asigna automáticamente los resultados de la ejecución de SQL a objetos Java.

2. ¿Cuál es la diferencia entre Hibernate y MyBatis?

Mismo punto:

  • Todos son encapsulaciones de jdbc y marcos aplicados a la capa de persistencia.

diferencia:

1) Relación de mapeo

  • MyBatis es un marco de mapeo semiautomático que configura la correspondencia entre los objetos Java y los resultados de la ejecución de declaraciones SQL, y es simple configurar relaciones de múltiples tablas.
  • Hibernate es un marco de mapeo de tablas completo que configura la correspondencia entre objetos Java y tablas de bases de datos, y configura tablas complejas con relaciones de múltiples tablas.

2) Optimización y portabilidad de SQL

  • Hibernate encapsula declaraciones SQL y proporciona funciones como registro, almacenamiento en caché y cascada (la cascada es más poderosa que MyBatis). Además, también proporciona HQL (Hibernate Query Language) para operar la base de datos. Tiene un buen soporte independiente de la base de datos, pero consumirá más rendimiento. Si el proyecto necesita admitir múltiples bases de datos, la cantidad de desarrollo de código será pequeña, pero la optimización de las declaraciones SQL será difícil .
  • MyBatis requiere escritura manual de SQL y admite SQL dinámico, procesamiento de listas, generación dinámica de nombres de tablas y soporte de procedimientos almacenados. La carga de trabajo de desarrollo es relativamente grande. El uso directo de declaraciones SQL para operar la base de datos no admite la independencia de la base de datos, pero es fácil optimizar las declaraciones SQL .

3) MyBatis e Hibernate tienen diferentes escenarios aplicables

  • Hibernate es un marco ORM estándar. Requiere menos escritura de SQL pero no es lo suficientemente flexible. Es adecuado para proyectos de software pequeños y medianos con requisitos relativamente estables, como los sistemas de automatización de oficinas.
  • MyBatis es un marco semi-ORM que requiere escribir más SQL, pero es más flexible y adecuado para proyectos con cambios frecuentes en la demanda e iteración rápida, como los sitios web de comercio electrónico.

3. ¿Proceso de uso de MyBatis? ¿ciclo vital?

El proceso básico de uso de MyBatis se puede dividir aproximadamente en los siguientes pasos:

imagen-20230820161432104

1) Crear SqlSessionFactory

SqlSessionFactory se puede crear desde la configuración o codificación directa

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2) Crear SqlSession a través de SqlSessionFactory

​ SqlSession (sesión) puede entenderse como el puente entre el programa y la base de datos.

SqlSession session = sqlSessionFactory.openSession();

3) Realizar operaciones de base de datos a través de sqlsession

Las sentencias SQL asignadas se pueden ejecutar directamente a través de la instancia SqlSession:

Blog blog = (Blog)session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

Una forma más común es obtener primero el Mapper y luego ejecutar la declaración SQL:

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

4) Llame a session.commit() para confirmar la transacción

​ Si se trata de una declaración de actualización o eliminación, también debemos enviar la transacción.

5) Llame a session.close() para cerrar la sesión

​ Finalmente, no olvides cerrar la sesión.

¿Ciclo de vida de MyBatis?

Varios componentes de MyBatis se mencionan anteriormente. En términos generales, el ciclo de vida de MyBatis es el ciclo de vida de estos componentes.

SqlSessionFactoryBuilder

  • Una vez creado SqlSessionFactory, ya no es necesario. Por lo tanto, el ciclo de vida de la instancia de SqlSessionFactoryBuilder solo existe dentro del método.

SqlSessionFábrica

  • SqlSessionFactory se utiliza para crear SqlSession, que es equivalente a un grupo de conexiones de base de datos. Cada vez que se crea SqlSessionFactory, se utilizarán recursos de la base de datos. La creación y destrucción múltiples son un desperdicio de recursos. Entonces SqlSessionFactory tiene un ciclo de vida a nivel de aplicación y debería ser un singleton.

sesión sql

  • SqlSession es equivalente a Connection en JDBC. Las instancias de SqlSession no son seguras para subprocesos y, por lo tanto, no se pueden compartir, por lo que su ciclo de vida óptimo es una solicitud o un método.

Mapeador

  • Mapper es una interfaz que vincula declaraciones de mapeo. La instancia de la interfaz del mapeador se obtiene de SqlSession, su ciclo de vida está dentro del método de transacción sqlsession y generalmente se controla a nivel de método.

imagen-20230820162114969

Por supuesto, todo se puede integrar con Spring. MyBatis generalmente se integra con Spring. Spring puede ayudarnos a crear SqlSession y mapeadores basados ​​en transacciones y seguros para subprocesos e inyectarlos directamente en nuestros beans. No necesitamos preocuparnos por ellos. El proceso de creación y el ciclo de vida son otra historia.

4. ¿Cómo pasar múltiples parámetros en el mapeador?

imagen-20230820162226243

Método 1: método de paso de parámetros secuencial

public User selectUser(String name, int deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>
  • #{}Los números del interior representan el orden de los parámetros pasados.
  • No se recomienda este método porque la expresión de la capa SQL no es intuitiva y es propensa a errores una vez que se ajusta el orden.

Método 2: método de paso de parámetros de anotación @Param

public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}El nombre dentro corresponde al nombre modificado en la anotación @Param entre paréntesis.
  • Este método es relativamente intuitivo cuando no hay muchos parámetros (recomendado).

Método 3: método de paso de parámetros de mapa

public User selectUser(Map<String, Object> params);

<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}Los nombres dentro corresponden a los nombres clave en el Mapa .
  • Este método es adecuado para pasar múltiples parámetros , y los parámetros son volátiles y se pueden pasar de manera flexible.

Método 4: método de paso de parámetros de Java Bean

public User selectUser(User user);

<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}Los nombres que contiene corresponden a los atributos de los miembros de la clase Usuario.
  • Este método es intuitivo y requiere crear una clase de entidad. No es fácil de extender y requiere agregar atributos. Sin embargo, el código es muy legible y la lógica de negocios es fácil de procesar. (Uso recomendado).

5. ¿Qué debo hacer si el nombre del atributo de la clase de entidad es diferente del nombre del campo en la tabla?

Tipo 1: al definir el alias del nombre del campo en la instrucción SQL de consulta , haga que el alias del nombre del campo sea coherente con el nombre del atributo de la clase de entidad.

<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
    select order_id id, order_no orderno, order_price price form orders 
    where order_id=#{id};
</select>

Tipo 2: utilice <resultado> en resultMap para asignar la correspondencia uno a uno entre los nombres de los campos y los nombres de los atributos de la clase de entidad.

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
	select * from orders where order_id=#{id}
</select>

<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">

    <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
    <result property ="orderno" column ="order_no"/>
    <result property="price" column="order_price" />
</resultMap>

6.¿Puede Mybatis asignar la clase de enumeración Enum?

  • Por supuesto, Mybatis puede asignar clases de enumeración, y no solo puede asignar clases de enumeración, sino que Mybatis puede asignar cualquier objeto a una columna de la tabla. El método de mapeo consiste en personalizar un TypeHandler e implementar el TypeHandler setParameter()y getResult()los métodos de interfaz.
  • TypeHandler tiene dos funciones:
    • Una es completar la conversión de javaType a jdbcType;
    • El segundo es completar la conversión de jdbcType a javaType.

Se materializa en dos métodos: setParameter()y getResult(), que representan respectivamente la configuración de los parámetros del marcador de posición del signo de interrogación de SQL y la obtención de los resultados de la consulta de la columna.

7. ¿Cuál es la diferencia entre #{} y ${}?

imagen-20230820163559653

  • #{} es un marcador de posición, precompilado; s{} es un empalmador, reemplazo de cadena, no precompilado.
  • Cuando Mybatis procesa #{}, el parámetro entrante #{} se pasa como una cadena. ¿Reemplazará #{} en SQL con? No., llame al método set de PreparedStatement para asignar valor.
  • #{} puede prevenir eficazmente la inyección de SQL y mejorar la seguridad del sistema; ${} no puede prevenir la inyección de SQL.
  • La sustitución de variables de #{} está en el DBMS; la sustitución de variables de s{} está fuera del DBMS.

DBMS: sistema de gestión de bases de datos

8. ¿Cómo escribir una consulta difusa como una declaración?

imagen-20230820163856729

  1. '%${question}%'Puede causar inyección SQL, no recomendado
  2. "%"#{question}"%"Nota: Debido a que cuando #{.…} se analiza en una declaración SQL, las comillas simples '' se agregarán automáticamente fuera de la variable, por lo que % necesita usar comillas dobles "" aquí, y las comillas simples no se pueden usar; de lo contrario, no se obtendrán resultados
    . ser encontrado.
  3. CONCAT('%', #{question},'%')Utilice la función CONCAT(), (recomendado)
  4. Utilice etiqueta de enlace (no recomendado)
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
	<bind name="pattern" value="'%' + username + '%'" />
	select id,sex,age,username,password from person where username LIKE #{pattern}
</select>

9. ¿Puede Mybatis realizar consultas relacionadas uno a uno y uno a muchos?

Por supuesto, no solo admite consultas relacionadas uno a uno y uno a muchos, sino que también admite consultas relacionadas muchos a muchos y muchos a uno.

imagen-20230820164506343

  • Uno a uno <asociación>

Por ejemplo, los pedidos y los pagos tienen una relación uno a uno. La realización de esta relación:

Clase de entidad:

public class Order {
    
    
    private Integer orderId;
    private String orderDesc;
    /**
    * 支付对象
    */
    private Pay pay;
    //……
}

Mapeo de resultados:

<!-- 订单resultMap -->
<resultMap id="peopleResultMap" type="cn.fighter3.entity.Order">
    <id property="orderId" column="order_id" />
    <result property="orderDesc" column="order_desc"/>
    
    <!--一对一结果映射-->
    <association property="pay" javaType="cn.fighter3.entity.Pay">
        <id column="payId" property="pay_id"/>
        <result column="account" property="account"/>
    </association>
</resultMap>

La consulta es una consulta de correlación ordinaria:

<select id="getTeacher" resultMap="getTeacherMap" parameterType="int">
    select * from order o
    left join pay p on o.order_id=p.order_id
    where o.order_id=#{orderId}
</select>
  • Uno a muchos <colección>

Por ejemplo, las categorías de productos y los productos tienen una relación de uno a muchos.

Clase de entidad:

public class Category {
    
    
    private int categoryId;
    private String categoryName;
    /**
    * 商品列表
    **/
    List<Product> products;
    //……
}

Mapeo de resultados:

<resultMap type="Category" id="categoryBean">
    <id column="categoryId" property="category_id" />
    <result column="categoryName" property="category_name" />
    
    <!-- 一对多的关系 -->
    <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
    <collection property="products" ofType="Product">
        <id column="product_id" property="productId" />
        <result column="productName" property="productName" />
        <result column="price" property="price" />
    </collection>
</resultMap>

La consulta es simplemente una consulta relacional ordinaria:

<!-- 关联查询分类和产品表 -->
<select id="listCategory" resultMap="categoryBean">
	select c.*, p.* from category_ c left join product_ p on c.id = p.cid
</select>

Entonces, ¿cómo implementar muchos a uno y muchos a muchos? Sigo utilizando <association> y <collection>. Debido a limitaciones de espacio, no entraremos en detalles aquí.

10. ¿Mybatis admite la carga diferida? ¿principio?

  • Mybatis admite la carga diferida de objetos de asociación y objetos de colección . La asociación se refiere a consultas uno a uno y la colección se refiere a consultas de uno a muchos. En el archivo de configuración de Mybatis, puede configurar si desea habilitar la carga diferida lazyLoadingEnabled=true|false .
  • Su principio es usar CGLIB para crear un objeto proxy del objeto de destino. Cuando se llama al método de destino, se ingresa el método interceptor . Por ejemplo , si a.getB().getName()el método interceptor invoke()encuentra a.getB()un valor nulo, entonces la consulta guardada previamente asociada con el El objeto B se enviará por separado sql, consulte B y luego llámelo a.setB(b), para que el atributo del objeto b de a tenga un valor y luego complete a.getB().getName()la llamada al método. Este es el principio básico de la carga diferida.
  • Por supuesto, no solo Mybatis, sino casi todos, incluido Hibernate, el principio de admitir la carga diferida es el mismo.

11. ¿Cómo obtener la clave primaria generada?

  • Agregue: keyProperty="ID" a la nueva etiqueta
<insert id="insert" useGeneratedKeys="true" keyProperty="userId" >
    insert into user(
    user_name, user_password, create_time)
    values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>
  • En este momento, puede completar el reabastecimiento de la clave principal.
mapper.insert(user);
user.getId;

12. ¿MyBatis admite SQL dinámico?

Hay algunas etiquetas en MyBatis que admiten SQL dinámico. Su principio es usar OGNL para calcular el valor de la expresión a partir del objeto de parámetro SQL y unir SQL dinámicamente de acuerdo con el valor de la expresión para completar la función de SQL dinámico.

OGNL: El nombre completo es Lenguaje de navegación de gráficos de objetos, que es un lenguaje de navegación de gráficos de objetos y un potente lenguaje de expresión de código abierto.

imagen-20230820170227229

  • si

Formar una cláusula donde basada en condiciones

<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = ‘ACTIVE’
    <if test="title != null">
    	AND title like #{title}
    </if>
</select>
  • elegir (cuando, de lo contrario)

Esto es algo similar a la declaración de cambio en Java.

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <choose>
        <when test="title != null">
        	AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
        	AND author_name like #{author.name}
        </when>
        <otherwise>
        	AND featured = 1
        </otherwise>
    </choose>
</select>
  • recortar(dónde, establecer)
  • <dónde> se puede utilizar cuando todas las condiciones de consulta son dinámicas
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG
    <where>
        <if test="state != null">
        	state = #{state}
        </if>
        <if test="title != null">
        	AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
        	AND author_name like #{author.name}
        </if>
    </where>
</select>
  • <set> se puede utilizar durante las actualizaciones dinámicas
<update id="updateAuthorIfNecessary">
    update Author
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email},</if>
        <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{id}
</update>
  • para cada

Lo sabrás cuando veas el nombre. Esto se utiliza para recorrer y recorrer la colección.

<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT * FROM POST P
    <where>
        <foreach item="item" index="index" collection="list"
            open="ID in (" separator="," close=")" nullable="true" >
            #{item}
        </foreach>
    </where>
</select>

13.¿Cómo realiza MyBatis operaciones por lotes?

imagen-20230820183750988

Primer método: usar la etiqueta foreach

foreach se utiliza principalmente para crear condiciones, puede iterar una colección en una declaración SQL.

Los atributos de la etiqueta foreach incluyen principalmente elemento, índice, colección, apertura, separador y cierre.

  • item representa el alias de cada elemento de la colección durante la iteración y es un nombre de variable aleatoria;
  • índice especifica un nombre para indicar la posición alcanzada por cada iteración durante el proceso de iteración, que no se usa comúnmente;
  • open indica con qué comienza la declaración, comúnmente usado "(";
  • separador indica qué símbolo se utiliza como separador entre cada iteración, comúnmente utilizado ",";
  • close indica con qué terminar, comúnmente usado ")".

Lo más crítico y más propenso a errores al usar foreach es el atributo de colección . Este atributo debe especificarse, pero en diferentes situaciones, el valor de este atributo es diferente. Hay tres situaciones principales:

  1. Si se pasa un solo parámetro y el tipo de parámetro es Lista, el valor del atributo de la colección es lista.
  2. Si se pasa un solo parámetro y el tipo de parámetro es una matriz, el valor del atributo de la colección es una matriz.
  3. Si se pasan múltiples parámetros, necesitamos encapsularlos en un mapa. Por supuesto, un solo parámetro también se puede encapsular en un mapa. De hecho, si pasa parámetros, también se encapsulará en MyBatis. Se convierte en un mapa, y la clave del mapa es el nombre del parámetro, por lo que en este momento, el valor del atributo de la colección es la clave del objeto de lista o matriz pasado en su propio mapa encapsulado.

Eche un vistazo a dos formas de guardar en lotes:

<!-- MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 --> //推荐使用
<insert id="addEmpsBatch">
    INSERT INTO emp(ename,gender,email,did)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
    	(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>
<!-- 这种方式需要数据库连接属性allowMutiQueries=true的⽀支持
如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->
<insert id="addEmpsBatch">
    <foreach collection="emps" item="emp" separator=";">
        INSERT INTO emp(ename,gender,email,did)
        VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>

Segundo método: use ExecutorType.BATCH

  • Hay tres ExecutorTypes integrados en Mybatis. El valor predeterminado es simple. En este modo, crea una nueva declaración de preprocesamiento para la ejecución de cada declaración y envía una única declaración SQL;
  • El modo por lotes reutiliza declaraciones preprocesadas y ejecuta todas las declaraciones de actualización en lotes. Obviamente, el rendimiento por lotes será mejor;
  • Pero el modo por lotes también tiene sus propios problemas: por ejemplo, durante la operación de inserción, no hay forma de obtener el ID incrementado automáticamente antes de enviar la transacción y, en algunos casos, no satisface las necesidades del negocio.

El uso específico es el siguiente:

//批量保存方法测试
@Test
public void testBatch() throws IOException{
    
    
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    //可以执行批量操作的sqlSession
    SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    //批量保存执行前时间
    long start = System.currentTimeMillis();
    try {
    
    
    	EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    	for (int i = 0; i < 1000; i++) {
    
    
            mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5),"b", "1"));
    	}
        openSession.commit();
        long end = System.currentTimeMillis();
        
        //批量保存执行后的时间
        System.out.println("执行时长" + (end - start));        
        //批量 预编译sql一次==》设置参数==》10000次==》执行1次 677
        //非批量 (预编译=设置参数=执行 )==》10000次 1121
    } finally {
    
    
    	openSession.close();
    }
}
  • mapper y mapper.xml son los siguientes:
public interface EmployeeMapper {
    
    
    //批量保存员工
    Long addEmp(Employee employee);
}
<mapper namespace="com.jourwon.mapper.EmployeeMapper">
    <!--批量量保存员⼯工 -->
    <insert id="addEmp">
        insert into employee(lastName,email,gender)
        values(#{lastName},#{email},#{gender})
    </insert>
</mapper>

14. ¿Hablar sobre el caché de primer y segundo nivel de Mybatis?

  1. Caché de primer nivel: caché local de HashMap basado en PerpetualCache. Su alcance de almacenamiento es SqlSession . Los cachés entre cada SqlSession están aislados entre sí . Cuando la sesión se vacía o cierra, todo el caché en SqlSession se vaciará. MyBatis abre el primero -Caché de nivel por defecto.

imagen-20230820185111588

  1. El caché de segundo nivel tiene el mismo mecanismo que el caché de primer nivel. De forma predeterminada, también utiliza almacenamiento PerpetualCache y HashMap. La diferencia es que su alcance de almacenamiento es Mapper (Namespace) , que se puede compartir entre múltiples SqlSession , y el La fuente de almacenamiento se puede personalizar, como Ehcache. El caché de segundo nivel no está habilitado de forma predeterminada. Para habilitar el caché de segundo nivel, debe implementar la interfaz de serialización serializable (que se puede usar para guardar el estado del objeto) para usar la clase de atributo de caché de segundo nivel. que se puede configurar en su archivo de mapeo.

imagen-20230820185247411

principio

15. ¿Puedes decirme cómo funciona MyBatis?

Ya tenemos una idea aproximada del flujo de trabajo de MyBatis, según el principio de funcionamiento, se puede dividir en dos pasos principales: 生成会话工厂, 会话运行.

imagen-20230820203332214

MyBatis es un marco maduro y el espacio es limitado. Centrémonos en lo grande, dejemos de lado lo pequeño y echemos un vistazo a su flujo de trabajo principal.

Construir fábrica de sesiones

La construcción de una fábrica de sesiones también se puede dividir en dos pasos:

imagen-20230820203453775

Obtener configuración:

El paso de obtener la configuración ha pasado por varias transformaciones, y finalmente se genera una instancia de configuración de clase de configuración, esta instancia de clase de configuración es muy importante, sus principales funciones incluyen:

  • Leer archivos de configuración, incluidos archivos de configuración básicos y archivos de mapeo
  • Inicialice la configuración básica, como el alias MyBatis y otros objetos de clase importantes, como complementos, mapeadores, ObjectFactory, etc.
  • Proporcionar un singleton como parámetro importante para la construcción de la fábrica de sesiones.
  • Su proceso de construcción también inicializará algunas variables de entorno, como fuentes de datos.
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    
    
    SqlSessionFactory var5;
    //省略异常处理
        //xml配置构建器
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        //通过转化的Configuration构建SqlSessionFactory
        var5 = this.build(parser.parse());
}
  • ConstruirSqlSessionFactory

SqlSessionFactory es solo una interfaz, y lo que se construye es en realidad una instancia de su clase de implementación. Generalmente usamos su clase de implementación DefaultSqlSessionFactory.

public SqlSessionFactory build(Configuration config) {
    
    
	return new DefaultSqlSessionFactory(config);
}

ejecución de sesión

La ejecución de sesiones es la parte más compleja de MyBatis y su funcionamiento no se puede separar de la cooperación de cuatro componentes principales:

imagen-20230820203959562

Para resumir la sesión ejecutada en su conjunto:

imagen-20230820204719649

Ejecutor

  • El Ejecutor juega un papel vital. SqlSession es solo una fachada, equivalente al servicio al cliente. El verdadero trabajo es el Ejecutor, como un ingeniero desconocido. Proporciona métodos de consulta y actualización correspondientes, así como métodos de transacción .
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//通过Configuration创建executor
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);

StatementHandler (controlador de sesión de base de datos)

  • StatementHandler, como sugiere el nombre, maneja sesiones de bases de datos . Tomemos SimpleExecutor como ejemplo y echemos un vistazo a su método de consulta: primero crea una instancia de StatementHandler y luego usa este controlador para ejecutar la consulta.
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    
    
    Statement stmt = null;
    
    List var9;
    try {
    
    
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
    
    
    	this.closeStatement(stmt);
    }
    return var9;
}
  • Echemos un vistazo a su método de consulta utilizando el PreparedStatementHandler más utilizado. De hecho, el prepareStatement anterior ya ha precompilado los parámetros. En este punto, SQL se ejecuta directamente y se utiliza ResultHandler para procesar los resultados devueltos.
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    
    
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

ParameterHandler (controlador de parámetros)

  • SQL está precompilado en PreparedStatementHandler
public void parameterize(Statement statement) throws SQLException {
    
    
	this.parameterHandler.setParameters((PreparedStatement)statement);
}
  • Aquí se utiliza ParameterHandler, y la función de setParameters es establecer los parámetros de la declaración SQL precompilada.
  • El procesador de tipos typeHandler también se utiliza para procesar tipos.
public interface ParameterHandler {
    
    
    Object getParameterObject();
    void setParameters(PreparedStatement var1) throws SQLException;
}

ResultSetHandler (controlador de resultados)

  • También hemos visto antes que el resultado final debe procesarse a través de ResultSetHandler, y el método handleResultSets se utiliza para envolver el conjunto de resultados.
  • Mybatis nos proporciona un DefaultResultSetHandler, que generalmente se usa para procesar resultados.
public interface ResultSetHandler {
    
    
    <E> List<E> handleResultSets(Statement var1) throws SQLException;
    <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
    void handleOutputParameters(CallableStatement var1) throws SQLException;
}

Utilizará typeHandle para procesar el tipo, luego ensamblará el objeto usando las reglas proporcionadas por ObjectFactory y lo devolverá a la persona que llama.

Finalmente, conectamos todo el flujo de trabajo y resumimos brevemente:

imagen-20230820204928378

  1. Lea el archivo de configuración de MyBatis —mybatis-config.xml, cargue el archivo de mapeo—el archivo de mapeo es el archivo de mapeo SQL y las declaraciones SQL para operar la base de datos están configuradas en el archivo. Finalmente, se genera un objeto de configuración .
  2. Construya una fábrica de sesiones : Construya la fábrica de sesiones SqlSessionFactory a través del entorno MyBatis y otra información de configuración.
  3. Crear objeto de sesión : el objeto SqlSession lo crea la fábrica de sesiones, que contiene todos los métodos para ejecutar declaraciones SQL .
  4. Ejecutor ejecutor : La capa inferior de MyBatis define una interfaz Ejecutor para operar la base de datos, generará dinámicamente declaraciones SQL que deben ejecutarse de acuerdo con los parámetros pasados ​​​​por SqlSession y también es responsable del mantenimiento del caché de consultas.
  5. StatementHandler: controlador de sesión de base de datos, que conecta en serie el procesamiento de mapeo de parámetros y el procesamiento de mapeo de resultados en ejecución .
  6. Procesamiento de parámetros: procese los tipos de parámetros de entrada y precompílelos .
  7. Procesamiento de resultados: procese el tipo de resultado devuelto y devuelva el objeto correspondiente de acuerdo con las reglas de mapeo de objetos .

16. ¿Cuál es la arquitectura funcional de MyBatis?

imagen-20230820205408347

Generalmente dividimos la arquitectura funcional de Mybatis en tres capas:

  • Capa de interfaz API : interfaz API proporcionada para uso externo. Los desarrolladores utilizan estas API locales para manipular la base de datos . Una vez que la capa de interfaz recibe la solicitud de llamada, llamará a la capa de procesamiento de datos para completar el procesamiento de datos específico.
  • Capa de procesamiento de datos : responsable de la búsqueda de SQL específica, el análisis de SQL, la ejecución de SQL y el procesamiento de mapeo de resultados de ejecución , etc. Su objetivo principal es completar una operación de base de datos de acuerdo con la solicitud de llamada.
  • Capa de soporte básico : Responsable del soporte funcional más básico, incluida la gestión de conexiones, la gestión de transacciones, la carga de configuración y el procesamiento de caché . Estas son cosas comunes y se extraen como los componentes más básicos. Proporcionar el soporte más básico para la capa superior de procesamiento de datos.

17. ¿Por qué la interfaz Mapper no requiere una clase de implementación?

Respuesta de cuatro palabras: proxy dinámico, echemos un vistazo al proceso de obtención de Mapper:

imagen-20230820205821566

  • ObtenerMapper

Todos sabemos que la interfaz Mapper definida no tiene una clase de implementación y el mapeo de Mapper en realidad se implementa a través de un proxy dinámico .

BlogMapper mapper = session.getMapper(BlogMapper.class);

Después de dar vueltas y vueltas para echar un vistazo, descubrí que el proceso de obtención de Mapper requiere primero obtener MapperProxyFactory: la fábrica de agentes Mapper.

  • MapperProxyFábrica

La función de MapperProxyFactory es generar MapperProxy (objeto proxy Mapper).

Aquí puede ver el enlace del proxy dinámico a la interfaz, su función es generar un objeto proxy dinámico (marcador de posición) y el método proxy se coloca en MapperProxy.

  • MapeadorProxy

En MapperProxy, generalmente se genera un objeto MapperMethod, que se inicializa mediante el método cachedMapperMethod y luego ejecuta el método de ejecución.

  • Método Mapper

El método de ejecución en MapperMethod realmente ejecutará sql. Aquí se utiliza el modo comando, de hecho, en un círculo, finalmente ejecuta el SQL del objeto a través de la instancia de SqlSession.

18. ¿Qué Ejecutores están disponibles en Mybatis?

imagen-20230820210336429

Mybatis tiene tres ejecutores básicos: SimpleExecutor, ReuseExecutor y BatchExecutor .

  • SimpleExecutor : Cada vez que se ejecuta una actualización o selección, se abre un objeto Declaración y el objeto Declaración se cierra inmediatamente después de su uso.
  • ReuseExecutor : Ejecute la actualización o seleccione, use SQL como clave para encontrar el objeto Declaración, úselo si existe y créelo si no existe. Después de su uso, el objeto Declaración no se cierra, sino que se coloca en Map <String, Declaración> para el próximo uso. En resumen, se trata de reutilizar objetos Statement .
  • BatchExecutor : ejecuta la actualización (sin selección, el procesamiento por lotes JDBC no admite la selección), agrega todo el sql al lote (addBatch()), espera la ejecución unificada (executeBatch()), almacena en caché varios objetos de declaración, cada uno después de los objetos de declaración se completan con addBatch (), esperan a que el procesamiento por lotes de ejecutarBatch () se ejecute uno por uno. Igual que el procesamiento por lotes JDBC.

Alcance del alcance: estas características de Executor están estrictamente limitadas dentro del alcance del ciclo de vida de SqlSession .

¿Cómo especificar qué Ejecutor usar en Mybatis?

  • En el archivo de configuración de Mybatis, puede especificar el tipo de ejecutor predeterminado ExecutorType en la configuración, o puede pasar manualmente los parámetros de tipo ExecutorType al método DefaultSqlSessionFactory para crear SqlSession, como sqlsession openSession(ExecutorType execType).
  • Configure el ejecutor predeterminado. SIMPLE es un ejecutor ordinario; el ejecutor REUSE reutilizará declaraciones preparadas; el ejecutor BATCH reutilizará declaraciones y realizará actualizaciones por lotes.

enchufar

19. Hable sobre el principio de funcionamiento del complemento Mybatis y cómo escribir un complemento.

¿Cómo funciona el complemento?

El funcionamiento de la sesión Mybatis requiere la cooperación de cuatro objetos principales: ParameterHandler, ResultSetHandler, StatementHandler y Executor. El principio del complemento es insertar parte de nuestro propio código al programar estos cuatro objetos.

imagen-20230820210956715

Mybatis utiliza el proxy dinámico de JDK para generar objetos proxy para los objetos de destino. Proporciona una clase de herramienta pluginque implementa InvocationHandlerla interfaz.

imagen-20230820211042127

Usando Pluginel objeto proxy generado, el objeto proxy ingresará el método de invocación al llamar al método. En el método de invocación, si hay un método de interceptación firmado, llamaremos aquí al método de intercepción del complemento y luego se obtendrá el resultado. ser devuelto. Si el método de firma no existe, el método que queremos ejecutar se llamará directamente mediante reflexión.

¿Cómo escribir un complemento?

Cuando escribimos el complemento MyBatis nosotros mismos, solo necesitamos implementar la interfaz del interceptor Interceptor(org.apache.ibatis.plugin Interceptor)y procesar los objetos y métodos de interceptación en la clase de implementación.

  • Implemente la interfaz Interceptor de Mybatis y reescriba intercept()el método

Aquí solo imprimimos antes y después de que el objeto de destino ejecute el método de destino;

public class MyInterceptor implements Interceptor {
    
    
    Properties props=null;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        System.out.println("before……");
        //如果当前代理的是一个非代理对象,那么就会调用真实拦截对象的方法
        // 如果不是它就会调用下个插件代理对象的invoke方法
        Object obj=invocation.proceed();
        System.out.println("after……");
        return obj;
    }
}
  • Luego escriba anotaciones para que el complemento determine los objetos que se interceptarán y los métodos que se interceptarán.
@Intercepts({
    
    @Signature(
        type = Executor.class, //确定要拦截的对象
        method = "update", //确定要拦截的⽅方法
        args = {
    
    MappedStatement.class, Object.class} //拦截⽅方法的参数
)})
public class MyInterceptor implements Interceptor {
    
    
    Properties props=null;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        System.out.println("before……");
        //如果当前代理的是一个非代理对象,那么就会调用真实拦截对象的方法
        // 如果不是它就会调用下个插件代理对象的invoke方法
        Object obj=invocation.proceed();
        System.out.println("after……");
        return obj;
    }
}
  • Finalmente, configure el complemento en el archivo de configuración de MyBatis.
<plugins>
    <plugin interceptor="xxx.MyPlugin">
    <property name="dbType",value="mysql"/>
    </plugin>
</plugins>

20. ¿Cómo realiza MyBatis la paginación? ¿Cuál es el principio del complemento de paginación?

¿Cómo pagina MyBatis?

MyBatis utiliza objetos RowBounds para la paginación, que es una paginación de memoria realizada para el conjunto de resultados ResultSet, en lugar de una paginación física . Puede escribir parámetros directamente con paginación física en SQL para completar la función de paginación física, o puede usar un complemento de paginación para completar la paginación física.

¿Cuál es el principio del complemento de paginación?

  • El principio básico del complemento de paginación es utilizar la interfaz de complemento proporcionada por Mybatis para implementar complementos personalizados e interceptar el método de consulta del Ejecutor.
  • Al ejecutar la consulta, intercepte el SQL que se ejecutará, luego reescriba el SQL y agregue las declaraciones de paginación física correspondientes y los parámetros de paginación física correspondientes de acuerdo con el dialecto.
  • Por ejemplo: select * from student, después de interceptar el sql, reescríbalo como:select t.* from (select * from student) t limit 0, 10

Fuente: El contraataque de la escoria de los fideos: ¡Veintidós imágenes, ocho mil palabras, veinte preguntas, derrotan completamente a MyBatis!

Supongo que te gusta

Origin blog.csdn.net/weixin_45483322/article/details/132396191
Recomendado
Clasificación