Operación básica de MyBatis y prueba unitaria SpringBoot

Tabla de contenido

1. ¿Qué son las pruebas unitarias?

1.1 Beneficios de las pruebas unitarias

1.2 Pasos de implementación de las pruebas unitarias

1.2.1 Generar clase de prueba unitaria:

1.2.2 Anotación @SpringBootTest

1.2.3 Resultados del método de prueba:

2. Utilice MyBatis para implementar operaciones de consulta

2.1 Consulta de tabla única

2.2 Marcadores de posición de parámetros #{} y ${}

2.2.1 Reemplazo directo del carácter ${}

2.2.2 #{} procesamiento de precompilación

2.2.3 La diferencia entre #{} y ${}

2.2.4 Problema de inyección SQL

2.3 como consulta 

2.3.1 Introducir concat para resolver el problema de #{} 

2.3.2 ¿Qué debo hacer si el nombre de la clase de entidad es diferente del nombre del campo de la base de datos? 

2.4 Consulta de varias tablas

3. Utilice MyBatis para implementar operaciones de modificación.

Cuarto, use MyBatis para implementar la operación de eliminación.

Cinco, use MyBatis para realizar la operación de suma


Prólogo: este artículo proviene del blog anterior del blogger Inicio rápido MyBatis . Las bases de datos para las siguientes operaciones se mencionan en el capítulo anterior y no se demostrarán aquí. 

Antes de presentar las pruebas unitarias, veamos un conjunto de operaciones:

Lo siguiente es consultar el nombre de usuario de acuerdo con la ID proporcionada:

Si necesita verificar la normalidad de la función sin utilizar pruebas unitarias, solo puede llamar a la capa Mapper a través del Servicio y luego llamar a la capa Servicio de acuerdo con la capa Controlador:

 El código se implementa de la siguiente manera:

1. ¿Qué son las pruebas unitarias?

Las pruebas unitarias se refieren al proceso de verificar y verificar la unidad comprobable más pequeña en el software, lo que se denomina prueba unitaria.

Cuando se crea un proyecto Spring Boot, el marco de prueba unitario spring-boot-test está predeterminado, y este marco de prueba unitario se implementa principalmente mediante otro marco de prueba conocido JUnit: 

Abra pom.xml y podrá ver que la siguiente información se agrega automáticamente cuando se crea el proyecto Spring Boot:

1.1 Beneficios de las pruebas unitarias

  1. Es muy sencillo, intuitivo y rápido comprobar si una determinada función es correcta.
  2. El uso de pruebas unitarias puede ayudarnos a encontrar algunos problemas al empaquetar, porque antes del empaquetamiento, todas las pruebas unitarias deben pasar; de lo contrario, el empaquetamiento no podrá tener éxito.
  3. Al utilizar pruebas unitarias, al probar funciones, puede probar funciones sin contaminar la base de datos conectada, es decir, sin realizar ningún cambio en la base de datos.

 Para el segundo punto:

1.2 Pasos de implementación de las pruebas unitarias

1.2.1 Generar clase de prueba unitaria:

 Después de seguir los pasos anteriores, se generará el siguiente código:

 1.2.2 Anotación @SpringBootTest

Recuerde agregar la anotación @SpringBootTest y luego mejorar la implementación específica del método de prueba:

package com.example.demo.mapper;

import com.example.demo.entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;
    @Test
    void getUserById() {
        UserEntity user = userMapper.getUserById(1);
        System.out.println(user);
    }
}

1.2.3 Resultados del método de prueba:

Si el parámetro en @Param se cambia a uid y otros lugares ya no se modifican, ¿el programa informará un error?

: Sí, los siguientes son los resultados de ejecución

analizar:

Específicamente, la anotación @Param se puede aplicar a los parámetros del método para especificar el nombre del parámetro. Este nombre se utilizará junto con ${} o #{} en la consulta SQL para que coincida con los parámetros correspondientes.

2. Utilice MyBatis para implementar operaciones de consulta

2.1 Consulta de tabla única

El código UserMapper.xml es el siguiente: 

 UserMapper.java es el siguiente:

2.2 Marcadores de posición de parámetros #{} y ${}

  • #{}: Procesamiento precompilado.
  • ${}: reemplazo directo de carácter. 

Procesamiento de precompilación : cuando MyBatis procesa #{}, reemplazará #{} en SQL con un número? y utilizará el método set de PreparedStatement para asignar valores.

Reemplazo directo : cuando MyBatis procesa ${}, reemplaza ${} con el valor de la variable.

Para observar mejor la diferencia entre los dos, necesitamos imprimir la declaración SQL ejecutada por MyBatis, antes de eso, debemos completar la siguiente configuración en application.properties:

#配置MyBatis的xml保存路径
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置打印打印的日志级别(默认的日志级别是info,需要设置为debug才能显示出来)
logging.level.com.example.demo=debug

2.2.1 Reemplazo directo del carácter ${}

Ejecute el siguiente método de prueba para verificar si la función es correcta y el código de ejecución es el siguiente:

 2.2.2 #{} procesamiento de precompilación

Ejecute el siguiente método de prueba para verificar si la función es correcta y el código de ejecución es el siguiente:

2.2.3 La diferencia entre #{} y ${}

Quizás después de observar los resultados de ejecución anteriores, no encontré ninguna diferencia entre los dos, esto se debe a que el tipo int se usaba para pasar parámetros antes, y cuando se usaba el tipo String para pasar parámetros, se enviarán los cambios.

El siguiente es el patrón de marcador de posición #{} y se puede observar que el programa se ejecuta normalmente.

Luego usamos ${} para reemplazar directamente, observamos los resultados de la ejecución y descubrimos que el programa informa un error:

Después de una cuidadosa observación, encontramos que esto se debe a que el formato de reemplazo directo se adopta sin ninguna modificación, por lo que el programa tiene un error, al igual que el siguiente código SQL:

 Esto se debe a que el reemplazo directo se usa sin comillas, por lo que hay un problema con el programa. La declaración SQL normal debería ser la siguiente:

 O puede hacerlo de esta manera para evitar el error de ahora:

El método de reemplazo directo adoptado por ${} introduce fácilmente problemas de inyección SQL. [se mencionará a continuación]

Además, existen diferencias de rendimiento entre los dos.

Cuando se usa #{}, el valor del parámetro se pasará a la base de datos como un parámetro precompilado y la base de datos procesará el parámetro de forma segura. Esto puede mejorar la eficiencia de ejecución de la base de datos, especialmente para declaraciones SQL ejecutadas con frecuencia, la base de datos puede reutilizar el plan de ejecución compilado, mejorando así el rendimiento de las consultas.

Cuando se usa ${}, el valor del parámetro se reemplazará directamente en la declaración SQL, lo que equivale a empalmar cadenas. Esto puede hacer que el plan de ejecución de la instrucción SQL se vuelva a compilar cada vez que se ejecuta, lo que reduce la eficiencia de ejecución de la base de datos.

Por lo tanto, desde el punto de vista del rendimiento, se recomienda usar #{} para procesar parámetros y tratar de evitar el uso de ${}. Especialmente cuando el valor del parámetro proviene de la entrada del usuario, el uso de ${} puede tener riesgos de seguridad y también puede reducir la eficiencia de ejecución de la base de datos.

Algunas personas pueden preguntar: dado que #{} tiene un rendimiento mayor que ${} y #{} también puede evitar la inyección de SQL, ¿cuál es el significado de #{}?

La razón por la que existe ${} es para satisfacer las necesidades de algunos escenarios especiales, que proporcionan mayor flexibilidad, por ejemplo, cuando algunos escenarios deben ordenarse según un determinado valor:

Aquí el programa reconoce desc como un valor de tipo String, pero en realidad es una palabra clave SQL. Según la lógica del programa, las comillas deben agregarse a la descripción "Tipo de cadena", pero no se agregan, por lo que aquí se informa un error.

Al usar ${}, el programa se ejecutará sin errores:

 resumen:

En resumen, #{} es un marcador de posición de parámetro seguro que puede prevenir ataques de inyección SQL, mientras que ${} es un reemplazo de cadena simple que debe usarse con precaución para evitar riesgos de seguridad. Al escribir declaraciones SQL, se recomienda usar #{} para procesar parámetros y tratar de evitar el uso de ${}, especialmente cuando el valor del parámetro proviene de la entrada del usuario.

ps: en MyBatis, ya sea #{} o ${}, la implementación subyacente es PreparedStatement, no Statement (Statemt ejecuta declaraciones SQL sin parámetros).

2.2.4 Problema de inyección SQL

La inyección de SQL es común al iniciar sesión. A continuación se muestran algunos ejemplos:

Observando la base de datos, podemos entender que la contraseña del usuario es admin: 

Si no ingresa la contraseña correcta cuando usa ${}, pero usa la inyección SQL, puede omitir la verificación y obtener información del usuario: 

 Si usa #{}, no habrá tal problema:

¿Por qué es esto? Aquí hay un conjunto de imágenes para explicarlo: 

 

 Cabe señalar que 1 es igual al símbolo 1. Aquí puede usar la declaración MySQL para una verificación adicional:

2.3 como consulta 

UserMapper.xml tiene este aspecto: 

 El código UserMapper.java es el siguiente:

El código de prueba es el siguiente:

Resultado de ejecución: (el programa tiene un error) 

2.3.1 Algunas personas pueden preguntar, ¿por qué no usar ${} para consultas similares?

Esto se debe a problemas relacionados principalmente con la inyección SQL y consultas difusas.

La inyección SQL entre ellos no se describirá en detalle, aquí comenzamos a hablar sobre el problema de las consultas difusas:

La consulta similar se utiliza generalmente para coincidencias aproximadas; los usuarios pueden ingresar varios comodines (como %y _) para realizar consultas aproximadas. Si se usa ${} para reemplazar directamente las condiciones de consulta ingresadas por el usuario, es posible que los comodines no funcionen o no cumplan con las expectativas, porque ${} es un reemplazo de texto y los comodines no se tratarán de manera especial. Cuando se utiliza #{}, el marco maneja los comodines correctamente, asegurando que funcionen como se espera.

'%' (signo de porcentaje): en la cláusula Me gusta, '%' representa un marcador de posición de cero o más caracteres. Coincide con cualquier secuencia de caracteres, incluido el carácter nulo. Aquí hay unos ejemplos:

  • '%apple%'Coincide con cualquier cadena que contenga una subcadena de "manzana", como "piña", "manzanas", "tarta de manzana", etc.

  • 'a%'Coincide con cualquier cadena que comience con la letra "a", como "manzana", "albaricoque", etc.

  • '%a'Coincide con cualquier cadena que termine con la letra "a", como "plátano", "atún", etc.

  • '%a%'Coincide con una cadena en cualquier lugar que contenga la letra "a", como "gato", "murciélago", "sombrero", etc.

_(guión bajo): en LIKEuna cláusula, _indica un marcador de posición de un carácter. Coincide sólo con un carácter. Aquí hay unos ejemplos:

  • 'a_ple'Coincide con "manzana", pero no con "aple" ni con "manzanas".

  • 'c_t'coincide con "gato", pero no con "carrito".

2.3.2 Introducir concat para resolver el problema de #{} 

A algunas personas les puede parecer extraño que obviamente no haya ningún problema con la operación, expliquemos el motivo a continuación: cuando se usa #{} para procesar, en realidad se convierte a esto en Mysql:

Introducción a concat 

  • En MySQL, la función concat se utiliza para concatenar varias cadenas en una sola. Toma dos o más argumentos, los concatena en orden y devuelve el resultado concatenado. 

Para evitar comillas simples redundantes, podemos usar concat para unirlas. El siguiente es un ejemplo: 

Se puede observar que se puede utilizar concat para unir estos parámetros, que pueden ser constantes de cadena, nombres de columnas o expresiones.

El siguiente es el resultado modificado de la consulta similar original usando concat:

Observe los resultados, el programa se ejecuta normalmente y los resultados de la consulta son consistentes con la base de datos:

2.3.3 ¿Qué debo hacer cuando el nombre de la clase de entidad es diferente del nombre del campo de la base de datos? 

En el proceso de desarrollo real, a menudo sucede que el nombre del campo establecido por la base de datos es diferente del nombre en nuestra clase de entidad (nota: si el caso es inconsistente, no afectará):

Dado que MyBatis es un marco ORM, si los dos son inconsistentes (no distinguen entre mayúsculas y minúsculas, por ejemplo, updateTime no se ve afectado), no se puede realizar la operación de mapeo. Ejecutamos el método de prueba y encontramos que el resultado del atributo pwd en la clase de entidad está vacío, verificando aún más el punto anterior: 

 Hay dos soluciones en este punto, una es usar la palabra clave as proporcionada por Mysql y la otra es usar resultMap.

① Utilice la palabra clave as para resolver los problemas anteriores, de la siguiente manera:

② Utilice el mapa de resultados:

 Aquí, las etiquetas id y result en resultMap se utilizan para completar el mapeo y se obtiene el valor de pwd:

 analizar:

1.Etiqueta de identificación: la etiqueta de identificación se utiliza para especificar la relación de mapeo de la columna de clave principal. Define la asignación de una columna en el resultado de la consulta al atributo de clave principal del objeto de destino. Un atributo de clave principal suele ser un atributo que identifica de forma única un objeto. En la etiqueta de identificación, se deben especificar dos atributos:

  • propiedad: especifica el nombre de propiedad del objeto de destino, es decir, el nombre de la propiedad de clave principal.
  • columna: especifica el nombre de la columna en el resultado de la consulta, es decir, el nombre de la columna de clave principal.

2.etiqueta de resultado: la etiqueta de resultado se utiliza para especificar la relación de mapeo de atributos comunes. Define la asignación de una columna en el resultado de la consulta a un atributo común del objeto de destino. Las propiedades ordinarias son propiedades distintas de las propiedades de clave principal de un objeto. En la etiqueta de resultado, se deben especificar dos atributos:

  • propiedad: especifica el nombre de propiedad del objeto de destino, es decir, el nombre de propiedad común.
  • columna: especifique el nombre de la columna en el resultado de la consulta, es decir, el nombre de la columna.

2.4 Consulta de varias tablas

Ingrese la identificación del autor para obtener información detallada del artículo. El siguiente es el código de implementación de ArticleMapper.xml:

 Código de implementación de ArticleMapper.java:

Las siguientes son las implementaciones de ArticleInfo y ArticleInfoVo:

 Ejecute el código de prueba, el efecto es el siguiente:

El análisis encontró que: solo las propiedades de ArticleInfoVo y no imprimió las propiedades de la clase principal.

El juicio preliminar se debe al uso del complemento Lombok, por lo que Articleinfovo solo imprime sus propios atributos y no los atributos de la clase principal cuando se usa el método toString:

 A continuación, verificamos el archivo de código de bytes en la carpeta de destino para verificar:

A través de la observación, estamos seguros de que este fenómeno se debe a nuestro uso del complemento Lombok.

Solución: reescriba el método toString en ArticleInfoVo (cuando utilice el complemento Lombok, si el método reescrito por usted mismo entra en conflicto con el método proporcionado por el complemento, prevalecerá el método escrito por usted mismo):

 Ps: asegúrese de elegir el tipo de anulación; de lo contrario, el método toString anulado no hereda las propiedades de la clase principal de forma predeterminada.

Ejecute el método de prueba nuevamente para imprimir las propiedades de la clase principal:

3. Utilice MyBatis para implementar operaciones de modificación.

Código de UserMapper.java:

Código de UserMapper.xml:

Nota : En la etiqueta update> de MyBatis <, la razón por la que no es necesario configurar el parámetro resultType es porque la etiqueta <update> generalmente se usa para realizar operaciones de actualización (como insertar, actualizar, eliminar) y devolver resultados. de estas operaciones suelen ser el número de filas afectadas, en lugar de un objeto de resultado concreto. 

Para probar, el efecto es el siguiente:

Dado que la operación de consulta se usa anteriormente y no implica operaciones como modificar la información de la base de datos, la anotación @Transactional no se menciona en la prueba unitaria. Aquí hay una breve introducción:

La anotación @Transactional es una anotación que se utiliza para declarar que un método o clase requiere gestión de transacciones. Se puede aplicar a nivel de método o de clase.

Cuando la anotación @Transactional se aplica a un método, indica que el método debe ejecutarse bajo control transaccional. Una transacción es un mecanismo utilizado para garantizar que un conjunto de operaciones tengan éxito o se reviertan. Durante la ejecución del método, si se produce una excepción, la transacción se revertirá; de lo contrario, se confirmará.

Después de agregar la anotación al método de prueba unitaria, realizará automáticamente una operación de reversión para garantizar que el método de prueba no contamine los datos en la base de datos: 

Puede abrir Mysql para una verificación adicional. El código anterior es para cambiar la contraseña del usuario 1 de 123456 a 1234567:

Se puede observar que los datos de la base de datos no han cambiado. 

Cuarto, use MyBatis para implementar la operación de eliminación.

UserMapper.java tiene este aspecto: 

 UserMapper.xml tiene este aspecto:

Para probar, el efecto es el siguiente:

La misma etiqueta <delete> se usa para realizar operaciones de eliminación, generalmente utilizada para eliminar registros de la base de datos, porque no devuelve ningún resultado, por lo que cuando se usa la etiqueta de eliminación en MyBatis, no es necesario configurar el parámetro 'resultType'.

Ps: el parámetro resultType se usa para especificar el tipo de resultado devuelto después de ejecutar la instrucción SQL. Generalmente se usa para operaciones de consulta y se usa para asignar resultados de consultas a objetos Java u otros tipos. Para la operación de eliminación, solo nos preocupamos por el número de filas eliminadas y no necesitamos obtener un objeto de resultado específico, por lo que no es necesario establecer el parámetro resultType.

Cinco, use MyBatis para realizar la operación de suma

UserMapper.java tiene este aspecto:

UserMapper.xml tiene este aspecto:

 Ejecute una prueba unitaria:

 Consultó Mysql para verificación y descubrió que efectivamente se había agregado un nuevo dato: 

Supongo que te gusta

Origin blog.csdn.net/qq_63218110/article/details/130834476
Recomendado
Clasificación