Explicação detalhada do Mybatis (super abrangente)

Índice:

1. Introdução ao MyBatis

1. O que é MyBatis

MyBatis é uma excelente estrutura de camada de persistência que oferece suporte a SQL personalizado, procedimentos armazenados e mapeamento avançado. O MyBatis elimina quase todo o código JDBC e o trabalho de definir parâmetros e obter conjuntos de resultados. MyBatis pode configurar e mapear tipos primitivos, interfaces e POJOs Java (Plain Old Java Objects, Plain Old Java Objects) como registros no banco de dados por meio de XML simples ou anotações.

2、MyBatis、Hibernate e JDBC

  1. Comparado com o Hibernate, o MyBatis tem encapsulamento menor que o Hibernate, mas tem excelente desempenho, tamanho pequeno, fácil de aprender e ampla aplicação

  2. Comparado com o JDBC, o MyBatis reduz a quantidade de código em mais de 50% e atende aos requisitos de alta simultaneidade e alta resposta

3. Vantagens e desvantagens

3.1 Vantagens

  • MyBatis é gratuito e de código aberto.
  • Comparado com JDBC, a quantidade de código é reduzida em mais de 50%.
  • MyBatis é o framework de persistência mais simples, pequeno e fácil de aprender.
  • O MyBatis é bastante flexível e não impõe nenhum impacto no design existente do aplicativo ou do banco de dados. O SQL é escrito em XML, separado do código lógico do programa, reduzindo o acoplamento, facilitando o gerenciamento e otimização unificados e melhorando a reutilização do código.
  • Fornece tags XML e oferece suporte à gravação de instruções SQL dinâmicas.
  • Fornece tags de mapeamento para oferecer suporte ao mapeamento de relacionamento de campo ORM entre objetos e bancos de dados.
  • Procedimentos armazenados são suportados. O MyBatis encapsula o SQL na forma de procedimentos armazenados, que podem manter a lógica de negócios fora do banco de dados, aprimorar a portabilidade dos aplicativos e torná-los mais fáceis de implantar e testar.

3.2 Desvantagens

  • Escrever instruções SQL requer muito trabalho, o que exige que os desenvolvedores tenham uma base sólida para escrever instruções SQL.
  • As instruções SQL dependem do banco de dados, resultando em baixa portabilidade do banco de dados e o banco de dados não pode ser substituído à vontade.

3.3 Cenários de Uso

O MyBatis se concentra no próprio SQL e é uma solução de camada DAO flexível o suficiente. É adequado para projetos com requisitos de alto desempenho e mudanças frequentes de requisitos, como projetos de Internet

4. Instalação

​ Para usar o MyBatis, basta colocar mybatis-x.x.x.jaro arquivo no classpath (classpath). Se você usar o Maven para construir o projeto, você precisa colocar o seguinte código de dependência no arquivo pom.xml

1. Por exemplo, em um projeto Maven, importe as dependências da versão MyBatis3.5.2

<!--MyBatis依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!--数据库依赖-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

2. O primeiro programa MyBatis

1. Crie SqlSessionFactory a partir de XML

Todo aplicativo baseado em MyBatis é centralizado em uma instância de SqlSessionFactory. Uma instância de SqlSessionFactory pode ser obtida por SqlSessionFactoryBuilder. O SqlSessionFactoryBuilder pode construir uma instância SqlSessionFactory a partir de um arquivo de configuração XML ou uma instância de configuração pré-configurada.

1.1 Configuração do XML

​O arquivo de configuraçãomybatis-config.xml contém as principais configurações do sistema MyBatis, incluindo a fonte de dados (DataSource) para obter a instância de conexão com o banco de dados e o gerenciador de transações (TransactionManager) que determina o escopo da transação e o método de controle. Os detalhes do arquivo de configuração XML serão discutidos posteriormente, aqui está um exemplo simples:

<?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配置内容时,有顺序限制-->
<configuration>
    <!--mybatis环境,default属性指定一个环境-->
    <environments default="development">
        <!--id属性自定义的环境唯一标识符-->
        <environment id="development">
            <!--指定使用jdbc事务管理-->
            <transactionManager type="JDBC"/>
            <!--使用mybatis内部带连接池功能的数据源-->
            <dataSource type="POOLED">
                <!--获取配置驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--获取配置url-->
                <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
                <!--获取配置账号-->
                <property name="username" value="root"/>
                <!--获取配置密码-->
                <property name="password" value="112112"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
    <mappers>
        <!--使用相对于类路径的资源引用  这是相对于类路径中-->
		<mapper resource="mapper/UserMapper.xml"/>
        <!--<mapper resource="com/hqyj/mapper/UserMapper.xml"/>-->
    </mappers>
</configuration>

1.1.1 Precauções

1. Preste atenção na declaração do cabeçalho XML, que é usado para verificar a exatidão do documento XML

2. O elemento mappers contém um conjunto de mappers (mapper), e os arquivos de mapeamento XML desses mappers contêm código SQL e informações de definição de mapeamento

3. Para mapeadores, ao <mapper resource="mapper/UserMapper.xml"/>importar arquivos XML, os arquivos XML estão sob os recursos do projeto maven ou o seguinte conteúdo precisa ser importado no arquivo pom.xml

<build>
    <!--resources配置解决mybatis 的mapperXml配置在java路径不被扫描的问题 -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!--跳过测试 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>

1.2 Obter SqlSessionFactory

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);

1.3 Tabela de banco de dados de design

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(255) NULL DEFAULT NULL,
  `state` int(255) NULL DEFAULT 1,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO `user` VALUES (1, 'admin', '111', 20, 1);
INSERT INTO `user` VALUES (2, '王五', '789', 24, 1);
INSERT INTO `user` VALUES (3, '小王', '123', 88, 1);

1.4 Escrevendo JavaBean

Primeiro, baixe o plugin Lombok no IDEA

Em seguida, importe as dependências em pom.xml

<!--Lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
</dependency>

Por fim, use anotações em vez de setters, getters, toString e construtores

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

1.5 Escrever UserMapper

<?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.hqyj.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.pojo.User">
        select * from `user`
    </select>
</mapper>

1.6 Teste

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL---方式一
// SqlSession执行文件中定义的SQL,并返回映射结果,参数可以直接输入方法名
// List<User> userList = sqlSession.selectList("com.hqyj.cl.mapper.UserMapper.getUserList");
// 执行SQL---方式二
// 通过SqlSession对象获取StudentMapper的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 使用代理mapper对象查询全部的用户信息
List<Student> studentList = mapper.selectAll();
// 输出内容
System.out.println(userList);

1.7 #{} e ${}

#{}É seguro passar valor no método e ele atua como um espaço reservado, que é um espaço reservado para parâmetros?, ou seja, pré-compilação sql, que pode impedir a injeção de sql

${}A substituição de strings, ou seja, sql splicing, não pode impedir a injeção de sql

1.7.1 Caso de injeção

Perceber:

O método de uso ${}obterá o conteúdo da propriedade de configuração em jdbc.properties, portanto, preste atenção

​ Se houver mais ou igual a 2 parâmetros na interface UserMapper, existem 3 maneiras de resolver o problema de que os parâmetros não podem ser encontrados

1. Encapsule em um método de coleta de mapa (para não mencionar por enquanto, é mais complicado)

2. Encapsule em um objeto de classe pojo correspondente (os parâmetros do método de login são substituídos diretamente por objetos de usuário)

3. Use a anotação @Param para especificar o nome do parâmetro para ajustar o método de interface do mapeador

​ login(@Param(“nome de usuário”)String nome de usuário, @Param(“senha”)String senha)

​Recomendação oficial: use anotações se o número de parâmetros for menor que 5 e use objetos se o número de parâmetros for maior que 5

List<Student> login(@Param("username")String username,@Param("password")String password);
<select id="login" resultType="com.hqyj.cl.pojo.Student">
    select * from `student` where username='${username}' and password='${password}'
</select>
public class TestStudentDao {
    
    
    public static void main(String[] args) throws IOException {
    
    
        InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        //可以调整login参数只有username以及mapper的sql 发现可以查出,但是2个参数不行
        List<Student>  student = mapper.login("admin","111222' or '1'='1");
        System.out.println(student);
    }
}

3. Objetos centrais

MyBatis tem três elementos básicos:

  • Interfaces e classes básicas
  • Arquivo de configuração principal do MyBatis (mybatis-config.xml)
  • Arquivo de mapeamento SQL (mapper.xml)

As principais interfaces e classes do MyBatis são as seguintes

                           bulid()                        openSession()
SqlSessionFactoryBuilder  --------->  sqlSessionFactory  --------------->  sqlSession 

Todo aplicativo MyBatis gira em torno de uma instância de um objeto SqlSessionFactory.

Primeiro obtenha o objeto SqlSessionFactoryBuilder, que pode ser construído de acordo com o arquivo de configuração XML ou a instância da classe Configuration.

Em seguida, obtenha o objeto SqlSessionFactory, que pode ser obtido por meio do objeto SqlSessionFactoryBuilder.

Com o objeto SqlSessionFactory, você pode obter a instância SqlSession. O objeto SqlSession contém completamente todos os métodos para executar operações SQL com o banco de dados como plano de fundo, e a instrução SQL mapeada pode ser executada diretamente com esta instância.

**Deve-se observar que:**Cada thread tem sua própria instância de SqlSession, e a instância de SqlSession não pode ser compartilhada nem é thread-safe. Portanto, o escopo de SqlSession está dentro do escopo da solicitação ou do corpo do método.

Solicitação de menção Relembre o redirecionamento e o encaminhamento aprendidos no estágio anterior. O redirecionamento não é a mesma solicitação, mas duas solicitações, e o escopo da solicitação não será compartilhado.

1、SqlSessionFactoryBuilder

SqlSessionFactoryBuilder gera SqlSessionFactory com base em informações de configuração ou código e fornece várias sobrecargas de método build(), que chamam o mesmo método de assinatura, a saber:

build(Reader reader, String environment, Properties properties)

Como o ambiente de parâmetros e as propriedades podem ser nulos, para remover métodos duplicados, na verdade existem apenas três métodos sobrecarregados, como a seguir:

  • build(InputStream inputStream, ambiente String, propriedades Properties)
  • build(Leitor Reader, Ambiente String, Propriedades Propriedades)
  • build(Configuração config)

Por meio da análise acima, descobriu-se que as informações de configuração podem ser fornecidas ao método build() de SqlSessionFactoryBuilder em três formas, a saber, InputStream (fluxo de bytes), Leitor (fluxo de caracteres) e Configuração (classe).

Como tanto o fluxo de bytes quanto o fluxo de caracteres pertencem à maneira de ler arquivos de configuração, é fácil pensar em duas maneiras de construir uma SqlSessionFactory, a saber: ler arquivos de configuração XML e escrever código. Geralmente é usado para construir SqlSessionFactory na forma de arquivos de configuração XML, de modo que, por um lado, a codificação rígida possa ser evitada e, por outro lado, seja conveniente para o futuro pessoal de configuração modificar e evitar a compilação repetida de código.

1.1 Ciclo de vida e escopo do SqlSessionFactoryBuilder

O maior recurso do SqlSessionFactoryBuilder é que ele é descartado após o uso. Após a criação do objeto SqlSessionFactory, esta classe não existe, pois o melhor escopo de SqlSessionFactoryBuilder existe no corpo do método, ou seja, variáveis ​​locais.

2、SqlSessionFactory

SqlSessionFactory é uma interface de fábrica em vez de uma classe real. Sua tarefa é criar SqlSession; todos os aplicativos MyBatis são centralizados na instância SqlSessionFactory e a instância de SqlSessionFactory pode ser obtida por meio do objeto SqlSessionFactoryBuilder

2.1 Ciclo de vida e escopo do SqlSessionFactory

Depois que um objeto SqlSessionFactory é criado, ele persiste em todo o aplicativo. Não há motivo para destruí-lo ou recriá-lo e não é recomendável criar o SqlSessionFactory várias vezes durante a execução de um aplicativo. Portanto, o melhor escopo de SqlSessionFactory é Application, que existe com o ciclo de vida do aplicativo. Esse modo "existe durante todo o tempo de execução do aplicativo e há apenas uma instância do objeto" é o chamado modo singleton (referindo-se a uma e apenas uma instância durante o tempo de execução).

3、SqlSession

SqlSession é um objeto usado para executar operações persistentes, semelhante a Connection em JDBC. Ele fornece todos os métodos necessários para executar comandos SQL no banco de dados e pode executar instruções SQL mapeadas diretamente por meio da instância SqlSession.

Existem dois usos principais de SqlSession:

  1. Obtenha um mapeador. Deixe o mapeador encontrar o SQL correspondente por meio do namespace e do nome do método, envie-o para o banco de dados e retorne o resultado após a execução.
  2. Execute o SQL diretamente através de "namespace (namespace) + SQL id", sem obter um mapeador.

3.1 Ciclo de vida e escopo do SqlSession

SqlSession corresponde a uma sessão de banco de dados. Como a sessão do banco de dados não é permanente, o ciclo de vida de SqlSession não é permanente e o objeto SqlSession precisa ser criado sempre que o banco de dados for acessado. Métodos de encapsulamento são recomendados para recuperar recursos.

4. Arquivo de configuração

No primeiro programa MyBatis, escrevemos o arquivo de configuração do MyBatis mybatis-config.xml, vamos falar sobre o conteúdo do arquivo de configuração em detalhes

1. Estrutura do arquivo de configuração do MyBatis

**Precauções: **Os nós dos elementos no arquivo mybatis-config.xml têm uma certa ordem, e as posições dos nós devem ser classificadas de acordo com as posições acima, caso contrário, haverá erros de compilação.

<?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 /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

1.1 etiqueta de configuração

O elemento de configuração é o nó raiz de todo o arquivo de configuração XML, e sua função é equivalente ao gerenciador do MyBatis, nele serão armazenadas todas as informações de configuração do MyBatis.

1.2 marca de propriedades

A marca de propriedades pode especificar um arquivo de propriedades externo (dbconfig.properties) por meio do atributo de recurso ou pode ser configurado por meio do subelemento de propriedades. do seguinte modo

<!--驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--获取连接对象地址-->
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
<!--数据库用户名-->
<property name="username" value="root"/>
<!--数据库密码-->
<property name="password" value="112112"/>

Faça referência às variáveis ​​acima no nó de ambientes

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

A partir da versão 3.4.2 do mybatis, a função de fornecer marcadores de atributo (conhecimento)
está desativada por padrão. Ative manualmente a configuração
1 e ative a configuração. <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
Se você usar o caractere ":" no nome do atributo (como: db: nome de usuário), ou em O operador ternário da expressão OGNL é usado no mapeamento SQL
(como: table N ame ! = null ? table N ame : 'globalconstants'), é necessário definir um atributo específico para modificar o caractere separando o nome do atributo e o valor padrão 2. Definindo Other configuration' < propertyname = " org . apache . ibatis . parsing . Property Parser . default − value − separator " value = " ? : " / > ' 3. Modifique e volte para a propriedade da conta ' < propertyname = " username " value = " {tableName != null ? tableName : 'global_constants'}), você precisa definir propriedades específicas para modificar os caracteres que separam o nome da propriedade e o valor padrão 2. Defina outro configuração `<property name="org.apache.ibatis.parsing.PropertyParser .default-value-separator" value="?:"/>` 3. Modifique a propriedade da conta` <property name="username" value="tab l e N ame ! _ _=n u ll ? N ome da tabela _ _ _:gl o ba l _co n s t a n t s ), você precisa definir propriedades específicas para modificar o caractere que separa o nome da propriedade e o valor padrão2, definir outras configurações'<propriedade deles _ _ _ _ _ _=" ou g . a p ach e . iba ti s . pars ing . Prop erty P a rser . d e f a ult _ _ _ _ _ _ _ _ _ _ _v a l u ese p a r a t ou " v a l u e=" ?:"/>'3. Modifique e volte para a propriedade do número da conta '<propriedade deles _ _ _ _ _ _=valor de " nome de usuario " _ _ _ _=" {username?:root}"/> 4、这样,如果没有设置`, é para usar root

1.3 guia de configurações

A tag de configurações é usada para configurar o comportamento do tempo de execução do MyBatis. Ela pode afetar profundamente a operação subjacente do MyBatis. Geralmente, muitas configurações não são necessárias e seu valor padrão pode ser usado na maioria dos casos. Na maioria dos casos, use-o para configurar logs, habilitar o mapeamento automático de regras de nomenclatura de hump e habilitar o cache de segundo nível (habilitado por padrão).

1.3.1 Ativar mapeamento automático de regras de nomenclatura camel case

<settings>
    <!-- 开启驼峰命名规则 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

1.3.2 Log de configuração

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>

1.3.3 Ativar cache L2

<settings>
    <!--开启二级缓存,默认是开启的,显示定义出来-->
    <setting name="cacheEnabled" value="true"/>
</settings>

1.4 tag typeAliases

Para não especificar o nome totalmente qualificado da classe em todos os lugares, podemos definir um alias usando a tag typeAliases. Dividido em duas formas:

  1. alias uma classe
<typeAliases>
    <!--方式一,逐个设置,大小写不受影响-->
    <typeAlias type="com.hqyj.cl.pojo.User" alias="User"/>
    <typeAlias type="com.hqyj.cl.pojo.Person" alias="Person"/>
</typeAliases>
  1. Alias ​​o conteúdo de uma pasta
<typeAliases>
    <!--方式二,统一设置
    在没有注解@Alias()的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
    比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值-->
    <package name="com.hqyj.cl.pojo"/>
</typeAliases>

1.5 tag typeHandlers

typeHandlers converte principalmente o valor adquirido em um tipo Java razoavelmente. No typeHandler, ele é dividido em jdbcType e javaType, entre os quais jdbcType é usado para definir o tipo de banco de dados e javaType é usado para definir o tipo Java, e o papel do typeHandler é realizar a conversão mútua entre jdbcType e javaType. Não precisamos configurá-lo manualmente porque MyBatis tem manipuladores de tipo padrão.

1.6 rótulo de ambientes

​ Na guia de ambientes, vários conjuntos de ambientes operacionais do MyBatis podem ser configurados para mapear o SQL para vários bancos de dados diferentes. Um ambiente operacional padrão (especificado por padrão) deve ser especificado. No momento, precisamos apenas configurar um conjunto de MySQL.

1.6.1 tag transactionManager

O MyBatis suporta dois gerenciadores de transações, JDBC e MANAGED. Nós escolhemos JDBC.

1.6.2 tag dataSource

​ Usado para configurar as propriedades de conexão do banco de dados, como o nome do driver, URL, nome de usuário e senha do banco de dados para se conectar, etc. Existem três valores (UNPOOLED, POOLED, JNDI), escolhemos POOLED

<!--数据库连接源-->
<dataSource type="POOLED">
    <!--获取配置驱动-->
    <property name="driver" value="${driver}"/>
    <!--获取配置url-->
    <property name="url" value="${url}"/>
    <!--获取配置账号-->
    <property name="username" value="${username!=null?username:root}"/>
    <!--获取配置密码-->
    <property name="password" value="${password}"/>
</dataSource>

1.7mapeamento resultMap (no arquivo Mapper.xml)

<!--定义结果集映射-->
<resultMap id="userMap" type="com.hqyj.pojo.User">
    <!--id主键 使用id标签-->
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
</resultMap>

1.9 marca de mapeadores

A tag mappers é usada para especificar o caminho do arquivo de mapeamento MyBatis SQL.

1.9.1 rótulo do mapeador

mapper é uma subtag de mappers. O atributo resource em mapper é usado para especificar o caminho do arquivo de mapeamento SQL (class resource path). Existem muitas maneiras, três das quais são usadas aqui

<!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
<mappers>
    <!--使用相对于类路径的资源引用,这是相对于类路径,UserMapper.xml存在resources下的mapper包中-->
    <mapper resource="mapper/UserMapper.xml"/>

    <!-- 使用映射器接口实现类的完全限定类名 mapper文件必须和接口在同一个包下,类名和mapper文件名必须相同-->
    <mapper class="com.hqyj.cl.mapper.UserMapper"/>

    <!-- 将包内的映射器接口实现全部注册为映射器 只需要指定到包名即可,mapper文件必须和接口在同一个包下,接口名和mapper文件名必须相同-->
    <package name="com.hqyj.cl.mapper"/>
</mappers>

Cinco, mapeador (mapeador)

​ O mapeador é o arquivo mais importante do MyBatis, que contém um conjunto de instruções SQL (como consultar, adicionar, excluir, modificar), que são chamadas de instruções de mapeamento ou instruções SQL de mapeamento.

O mapeador consiste em uma interface Java e um arquivo XML (ou anotação), e suas funções são as seguintes:

  • Definir tipo de parâmetro
  • Configure o cache, o padrão é o cache de primeiro nível
  • Forneça instrução SQL e SQL dinâmico
  • Defina o relacionamento de mapeamento entre os resultados da consulta e o POJO

O mapeador tem as duas implementações a seguir

  • É realizado por arquivo XML, como o arquivo XML que descrevemos no arquivo mybatis-config.xml, que é usado para gerar o mapeador. maneira recomendada.
  • Realizado por anotação, utilize o objeto Configuration para cadastrar a interface Mapper.

1. Mapeador de implementação XML

O mapeador de definição XML é dividido em duas partes: interface e XML.

1.1 Definir a interface

public interface UserMapper {
    
    
    List<User> getUserList();
}

1.2 Definir UserMapper.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.hqyj.cl.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.cl.pojo.User">
        select * from `user`
    </select>
</mapper>

1.2.1 Descrição

  1. namespace é usado para definir um namespace, que é consistente com o nome totalmente qualificado da interface definida.
  2. <select> O elemento indica que esta é uma instrução de consulta e o atributo id é usado para identificar este SQL. resultType indica que o que é retornado é um valor do tipo User.

1.3 Modifique o arquivo de configuração do MyBatis

Adicione o seguinte código ao arquivo de configuração MyBatis

<!--该语句用来引入 XML 文件,MyBatis 会读取 UserMapper.xml 文件,生成映射器-->
<mapper resource="com/hqyj/cl/mapper/UserMapper.xml"/>

1.4 Código de teste

@Test
public void getUserList() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 通过SqlSession对象获取StudentMapper的代理对象
    StudentMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 使用代理mapper对象查询全部的用户信息
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
}

2. A anotação implementa o mapeador

Para implementar o mapper usando anotações, você só precisa usar anotações Java na interface e injetar SQL

2.1 Código de teste

public interface UserMapper {
    
    
    @Select(value = "select * from user")
    public List<User> getUserList();
}

2.1.1 Descrição

  1. Usamos a anotação @Select e injetamos a mesma instrução select do XML.
  2. Se as anotações e os arquivos XML forem usados ​​para definir ao mesmo tempo, o método XML substituirá o método de anotação. (Use apenas um método de configuração e erros podem ser relatados)
  3. Para instruções simples, você pode usar anotações e, para instruções complexas, é recomendável usar XML.

3. Elementos principais do mapeador MyBatis

Isso será explicado um por um no estudo de acompanhamento

nome do elemento descrever Observação
mapeador O nó raiz do arquivo de mapeamento, com apenas um atributo chamado namespace O papel do namespace é o seguinte: ele é usado para distinguir diferentes mapeadores e vincula globalmente a interface DAO, ou seja, programação orientada a interface. Quando um namespace é vinculado a uma interface, você não precisa escrever a classe de implementação da interface. MyBatis encontrará a configuração do mapeador correspondente por meio do nome totalmente qualificado da interface para executar a instrução SQL. Portanto, o nome do namespace deve ser igual ao nome da interface.
selecionar Instrução de consulta, um dos elementos mais comumente usados ​​e complexos Você pode personalizar parâmetros, retornar conjuntos de resultados, etc.
inserir inserir declaração Retorna um inteiro após a execução, representando o número de itens inseridos
atualizar declaração de atualização Retorna um inteiro após a execução, representando o número de itens atualizados
excluir excluir declaração Retorna um inteiro após a execução, representando o número de itens excluídos
parametroMapa Definir a relação de mapeamento de parâmetros Elemento a ser removido, obsoleto
sql Permite definir uma parte do SQL e, em seguida, referir-se a ela em qualquer lugar Por exemplo, um nome de coluna de tabela, podemos defini-lo uma vez e usá-lo em várias instruções SQL
resultadoMapa Usado para descrever a correspondência entre conjuntos de resultados de banco de dados e objetos, é o elemento mais complexo e poderoso Fornecer regras de mapeamento
cache Configurar o cache para um determinado namespace -
cache-ref Referências a outras configurações de cache de namespace -

Seis, MyBatis executa SQL de duas maneiras

O MyBatis possui duas formas de executar instruções SQL, conforme a seguir:

  1. Enviar SQL por SqlSession
  2. Obtenha a interface Mapper por meio de SqlSession e envie SQL por meio da interface Mapper

1、SqlSession

Depois que houver um mapeador, você pode enviar SQL por meio de SqlSession.Existem dois métodos de consulta comuns em MyBatis, ou seja, selectOne e selectList.

1.1 selecioneUm

O método selectOne indica que a consulta é utilizada e apenas um objeto é retornado e a condição da consulta deve ser especificada. Apenas 0 ou 1 registro pode ser consultado, e se houver mais de 1 registro, ocorrerá erro de operação. Os formatos comuns são os seguintes (existem outros métodos de sobrecarga, escolha de acordo com suas necessidades)

sqlSession.selectOne(String arg0, Object arg1)

1.2 lista de seleção

O método selectList representa uma consulta e retorna uma lista. 0 ou N registros podem ser consultados. Os formatos comuns são os seguintes

sqlSession.selectList(String arg0, Object arg1)

1.3 Descrição

  1. No formato de sintaxe acima, o objeto String é composto por um namespace e um id SQL, que localiza completamente um SQL, de modo que o MyBatis encontre o SQL correspondente. O objeto Object é o parâmetro que precisa ser passado, ou seja, a condição da consulta
  2. O selectList implementado por selectOne pode ser implementado, ou seja, existe apenas um objeto na lista. Mas o que selectList pode alcançar, selectOne pode não ser capaz de alcançar
  3. Se houver apenas um SQL cujo id seja getUserList em MyBatis, o nome do caminho do pacote poderá ser removido

2. Interface do mapeador

SqlSession 还可以获取 Mapper 接口,通过 Mapper 接口发送 SQL,如下所示

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

2.1 说明

  1. 通过 SqlSession 的 getMapper 方法获取一个 Mapper 接口,然后就可以调用它的方法了。因为 XML 文件或者接口注解定义的 SQL 都可以通过“类的全限定名+方法名”查找,所以 MyBatis 会启用对应的 SQL 运行,并返回结果。

七、CRUD标签

​ 只有 select 标签有 resultType 属性,因为只有查询操作才需要对返回结果类型进行相应的指定。

1、select标签

在 MyBatis 中,select 标签是最常用也是功能最强大的 SQL 语言标签,用于执行查询操作。格式如下

<select id="queryUserByUsername" resultType="user" parameterType="string">
    select * from `user` where `username` like concat ('%',#{username},'%')
</select>

1.1 说明

  1. 执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如JavaBean、Map 等。MyBatis 提供了强大的映射规则,执行 SQL 后,MyBatis 会将结果集自动映射到 JavaBean 中。
  2. 为了使数据库的查询结果和返回值类型中的属性能够自动匹配,通常会对 MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则,这样就不需要再做映射了(数据库表字段名和属性名不一致时需要手动映射)
  3. 参数的传递使用#{参数名},相当于告诉 MyBatis 生成 PreparedStatement 参数。

1.2 常用属性

属性名称 描 述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+id不唯一,那么 MyBatis 抛出异常
parameterType 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 支持基本数据类型和 JavaBean、Map 等复杂数据类型
resultType SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一 -
resultMap 它是映射集的引用,与 <resultMap> 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等
flushCache 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存
useCache 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中 -
timeout 用于设置超时参数,单位是秒(s),超时将抛出异常 -
fetchSize 获取记录的总条数设定 默认值是数据库厂商提供的 JDBC 驱动所设置的条数
statementType 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement) -
resultSetType 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新) -

1.3 多参数

​ 有的时候,我们需要传递多个参数来执行SQL语句,比如用户登录;这个时候,有三种方式可以实现,分别是使用Map传递参数、使用注解传递参数、使用JavaBean传递参数;考虑到性能等问题,不推荐使用Map传递参数,所以,我们现在对后面两种情况进行分析

1.3.1 使用注解传递参数

Use a anotação @Param() do MyBatis para passar parâmetros, conforme abaixo

1.3.1.2 Caso
  1. interface
//用户登录
User login(@Param("username")String username,@Param("password")String password);
  1. mapeador
<!--用户登录-->
<select id="login" resultType="User">
    select * from user where username = #{username} and password = #{password}
</select>
1.3.1.2 Descrição

​ Ao passarmos o parâmetro para segundo plano, o MyBatis saberá que #{name} representa o parâmetro name através do nome fornecido por @Param, o que melhora a legibilidade do parâmetro. Mas se esse SQL tiver uma consulta com 10 parâmetros, isso reduzirá a legibilidade e aumentará a complexidade do código.

1.3.2 Usando JavaBean para passar parâmetros

No caso de muitos parâmetros, o MyBatis permite organizar um JavaBean e definir parâmetros por meio de métodos setter e getter simples para melhorar a legibilidade. Do seguinte modo

1.3.2.1 Caso
  1. interface
//增加用户
int selectUser(User user);
  1. mapeador
<!--增加用户-->
<select id="selectUser" resultType="User">
    select * from user where username = #{username} and password = #{password} and position = #{position}
</select>

1.3.3 Diferenças

A diferença entre os dois métodos acima é a seguinte.

  • Passar parâmetros usando a anotação @Param será afetado pelo número de parâmetros. Quando n≤5, é a melhor forma de passar parâmetros, pois é mais intuitivo; quando n>5, múltiplos parâmetros trarão dificuldades na chamada.
  • Quando o número de parâmetros for maior que 5, recomenda-se a utilização do método JavaBean.

2. inserir etiqueta

A tag de inserção MyBatis é usada para definir a instrução de inserção e executar a operação de inserção. Quando MyBatis executa uma instrução de inserção, ele retorna o número de linhas que afeta o banco de dados.

2.1 Caso

Adicionar função de usuário por meio da tag de inserção

  1. interface
//增加用户
int addUser(User user);
  1. mapeador
<!--增加用户-->
<insert id="addUser">
    insert into user values (null,#{username},#{password},#{userPhone},#{createTime},#{position})
</insert>

aula de teste

sqlSession.commit(); requer confirmação

2.2 Propriedades comuns

Nome do Atributo descrever Observação
eu ia Ele é usado em combinação com o namespace do Mapper e é um identificador exclusivo para MyBatis chamar MyBatis lança uma exceção se o namespace + id não for exclusivo
tipo de parâmetro O nome completo ou alias do tipo de parâmetro passado para a instrução SQL, que é um atributo opcional. Suporta tipos de dados básicos e tipos de dados complexos, como JavaBean e Map
keyProperty A função deste atributo é atribuir o valor de retorno da operação de inserção a um atributo da classe PO, geralmente o atributo correspondente à chave primária. Se for uma chave primária conjunta, vários valores podem ser separados por vírgulas. -
useGeneratedKe 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。 -
databaseId 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'"> 来为特定数据库指定不同的 sql 语句。 MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
keyColumn 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。 -

2.3 多参数

和select标签规则一样

3、update标签

MyBatis update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

3.1 案例

  1. 接口
//通过用户姓名修改用户
int updateUserByUserName(User user);
  1. Mapper
<!--通过用户姓名修改用户-->
<update id="updateUserByUsername">
    update user set phone = #{phone},password = #{password} where username = #{username}
</update>

3.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

3.3 多参数

和select标签规则一样

4、delete标签

MyBatis delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

4.1 案例

  1. 接口
//删除某个用户
int delUserById(int id);
  1. Mapper
<!--删除某个用户-->
<delete id="delUserById">
    delete from user where id = #{id}
</delete>

4.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

4.3 多参数

和select标签规则一样

八、resultMap

​ resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。现有的 MyBatis 版本只支持 resultMap 查询,我们通过案例分析

1、构成

<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
    <idArg/><!-- ID参数,结果为ID -->
    <arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
    <case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>

1.1 说明

  • <resultMap>元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识
  • 子元素 <constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。一般不用
  • 子元素 <id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
  • 子元素<result> 用于表示 POJO 和 SQL 列名的映射关系。
  • 子元素 <association><collection><discriminator> 用在级联的情况下。后续学习

id 和 result 元素都有以下属性

元素 说明
property 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上
column 对应 SQL 列
javaType 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名
jdbcType 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型
typeHandler 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则

1.2 获取结果集的两种方式

1.2.1 使用Map存储结果集

不推荐使用该方式

  1. 接口
// 用户列表
List<Map<String,Object>> queryAllUser();
  1. Mapper
<select id="selectAllUser" resultType="map">
    select * from `user`
</select>

**注意事项:**Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。

1.2.2 使用POJO存储结果集

推荐使用该方式

  1. POJO类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String username;
    private String password;
}
  1. 接口
public interface UserMapper {
    
    
    List<User> getUserList();
}
  1. 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="com.hqyj.cl.dao.UserDao">
    <resultMap id="userMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="userName"/>
        <result column="password" property="password"/>
    </resultMap>
	<!--查询用户列表-->
    <select id="getUserList" resultMap="userMap">
        select * from `user`
    </select>
</mapper>
1.2.2.1 说明
  1. resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
  2. 使用 property 元素指定 User的属性名称 username,column 表示数据库中 user表的 SQL 列名 username,将 POJO 和 SQL 的查询结果一 一对应。
  3. MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
  4. resultMap 和 resultType 不能同时使用。

九、注解

为了简化 XML 的配置,MyBatis 提供了注解,这些注解主要分为三大类,即 SQL 语句映射、结果集映射和关系映射。我们就逐一学习

1、SQL 语句映射

1.1 @Insert

实现新增功能

@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age})")
int addUser(User user);

1.2 @SelectKey

插入后,获取id的值

@Insert("insert into user(id,name) values(#{id},#{name})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int.class,before = false)
int insert(User user);

@SelectKey 各个属性含义如下

  • statement:表示要运行的 SQL 语句;
  • keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
  • keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
  • resultType:指定 SQL 语句的返回值;
  • before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。

1.3 @Select

实现查询功能

增加templateMapper.xml文件在mapper包中 namespace属性空着

mybatis-config.xml中要扫描包

@Select("Select * from user")
@Results({
    
    
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "name", property = "name"),
    @Result(column = "sex", property = "sex"),
    @Result(column = "age", property = "age")
})
List<User> queryAllUser();

@Results配置映射关系,如果mybatis-config.xml中关闭了驼峰命名,数据库字段与pojo类成员变量不能自动映射可以使用该注解

1.4 @Update

实现更新功能

@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);

1.5 @Delete

实现删除功能

@Delete("delete from  user  where id =#{id}")
void deleteById(Integer id);

1.6 @Param

用于在 Mapper 接口中映射多个参数,之前已经说明并演示

2、结果集映射

@Result、@Results、@ResultMap 是结果集映射的三大注解。

2.1 案例

@Select({
    
    "select id, name, class_id from student"})
@Results(id="studentMap", value={
    
    
    @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
    @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
    @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAllStudent();

@Results 各个属性的含义

  • id:表示当前结果集声明的唯一标识,可以通过id重复利用;
  • value:表示结果集映射关系;
  • @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。

使用 @ResultMap 来引用映射结果集,其中 value 可省略

@Select({
    
    "select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(int id);

3、关系映射

基础学生类增加成员变量修改数据类型,记得要重构有参构造方法。

3.1 @one

用于一对一关系映射

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
   @Result(property="addr",column="fk_addr_id",one=@One(select="com.hqyj.cl.mapper.StudentMapper.getAddr"))
})
List<Student> getAllStudents();

@Select("select * from addr where id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="addrName",column="addr_name")
})
Addr getAddr(int id);

3.2 @many

用于一对多关系映射

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
  @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher where id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name")
})
List<Teacher> getTeacher(int id);

3.3 多对多

在一对多的基础上,增加一个外表student_teacher,实现对student表和teacher表的关联

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),    @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher t,student_fk_teacher st where t.id = st.teacher_id and st.student_id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name"),
})
List<Teacher> getTeacher(int id);

十、MyBatis关联(级联)查询

级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。比如一个人对应一张身份证,一张身份证对应一个人、一个班级拥有多个学生,一个学生只能够属于某个班级、 一个学生可以选修多门课程,一个课程可以被多个学生选修。

1、一对一

在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理一对一级联关系。示例代码如下

POJO类和数据库设计,Student、Teacher、Addr三个类

  1. Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private int id;
    private String studentName;
    private Addr addr;
    private List<Teacher> teacher;
}
  1. Addr类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Addr {
    
    
    private int id;
    private String addr;
}
  1. 接口
List<Student> selectAllStudent();
  1. 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="com.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <association property="addr">
            <id property="id" column="id"/>
            <result property="addr" column="addr"/>
        </association>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a where s.fk_addr_id = a.id 
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<association> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

2、一对多

在 MyBatis 中,通过 <resultMap> 元素的子元素 <collection> 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下

  1. Teacher类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    
    
    private int id;
    private String teacherName;
}
  1. 接口
List<Student> selectAllStudent();
  1. 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="com.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <collection property="teacher" ofType="teacher">
            <id property="id" column="id"/>
            <result property="teacherName" column="teacher_name"/>
        </collection>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a,`teacher` t where s.fk_teacher_id = t.id
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<collection> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

3、多对多

​ 实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

十一、MyBatis动态SQL

​ 动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。动态 SQL 大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。MyBatis 也可以在注解中配置 SQL,但是由于注解功能受限,且对于复杂的 SQL 语句来说可读性差,所以使用较少。

1、动态SQL元素

MyBatis 的动态 SQL 包括以下几种元素

元素 作用 备注
if 判断语句 单条件分支判断
choose(when、otherwise) 相当于 Java 中的 switch case 语句 多条件分支判断
trim、where 辅助元素 用于处理一些SQL拼装问题
foreach 循环语句 在in语句等列举条件常用
bind 辅助元素 拼接参数

1.1 if标签

MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。

1.1.1 格式

<if test="判断条件">
    <!--条件为true时执行SQL语句-->
    SQL语句
</if>

1.1.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <if test="name != null">
        and `name` like #{name}
    </if>
    <if test="state != 0">
        and state like #{state}
    </if>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("王五");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.2 choose、when和otherwise标签

​ MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if><else></else> </if> 的效果,可以借助 <choose><when><otherwise> 来实现。

1.2.1 格式

​ choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose为switch,when 为 case,otherwise则为default。

<choose>
    <when test="判断条件1">
        SQL语句1
    </when >
    <when test="判断条件2">
        SQL语句2
    </when >
    <when test="判断条件3">
        SQL语句3
    </when >
    <otherwise>
        SQL语句4
    </otherwise>
</choose>

1.2.1 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <choose>
        <when test="name != null and name !=''">
            AND name LIKE CONCAT('%',#{name},'%')
        </when>
        <when test="state != 0 and state == 1">
            AND state = #{state}
        </when>
        <otherwise>
            AND password is not null
        </otherwise>
    </choose>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.3 where标签

​ 在上述标签SQL代码的编写中,我们不难发现,每一条SQL 语句中加入了一个条件“1=1”,如果没有加入这个条件,那么可能就会变成下面这样一条错误的语句。

SELECT * FROM user AND name LIKE CONCAT('%',#{name},'%')

显然以上语句会出现 SQL 语法异常,但加入“1=1”这样的条件又非常奇怪,所以 MyBatis 提供了 where 标签

1.3.1 格式

where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件,语法如下

<where>
    <if test="判断条件">
        AND/OR ...
    </if>
</where>

**注意事项:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句**的 AND 或者 OR 关键词去掉。

1.3.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <where>
        <if test="name != null">
            name like #{name}
        </if>
        <if test="state != 0">
            AND state like #{state}
        </if>
    </where>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
	user.setName("admin");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.4 set标签

​ 在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号

1.4.1 案例

  1. 接口
int updateUser(User user);
  1. Mapper
<update id="updateUser">
    UPDATE user
    <set>
        <if test="password!=null">
            password=#{password}
        </if>
        <if test="state==0 || state==1">
            state=#{state}
        </if>
    </set>
    WHERE name=#{name}
</update>
  1. 测试
@Test
public void updateUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("admin");
    user.setState(1);
    int result = mapper.updateUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

**注意事项:**if判断中必须成立一个,并且where后面的条件对应的值必须传递,否则报错

1.5 foreach标签

​ 对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。 Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
    参数值
</foreach>

foreach 标签主要有以下属性,说明如下。

  • item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选
  • index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
  • open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
  • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
  • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
  • collection表示迭代集合的名称,可以使用@Param注解指定,有以下四种情况
    • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list;
    • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array;
    • 如果如果传入的是单参数且参数类型是一个Set集合的时候,可以通过注解@Param("set")指定key值;
    • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。必须加上注解@Param指定

1.5.1 案例

  1. 接口
List<User> selectUser(List<Integer> ageList);
  1. Mapper
<select id="selectUser" resultMap="userMap">
    SELECT * FROM user WHERE age in
    <foreach item="age" index="index" collection="list" open="("
             separator="," close=")">
        #{age}
    </foreach>
</select>
  1. 测试
@Test
public void selectUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Integer> ageList = new ArrayList<>();
    ageList.add(1);
    ageList.add(12);
    ageList.add(13);
    List<User> result = mapper.selectUser(ageList);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

1.6 bind标签

​ 每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

MyBatis bind 标签可以通过 OGNL(对象导航图语言) 表达式自定义一个上下文变量。

bind 元素属性如下

  • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
  • name:给对应参数取的别名。

1.6.1 案例

  1. 接口
List<User> queryUser(String name);
  1. Mapper
<select id="queryUser" resultMap="userMap">
    <bind name="pattern" value="'%'+_parameter+'%'" />
    SELECT * FROM `user`
    WHERE name like #{pattern} 
</select>
  1. 测试
@Test
public void queryUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.queryUser("a");
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

**注意事项:**以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,提高了可移植性。

1.7 trim标签

​ 在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

1.7.1 格式

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>
1.7.1.1 属性说明
属性 描述
prefix 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
suffix 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
prefixOverrides 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
suffixOverrides 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定

1.7.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <trim prefix="where" prefixOverrides="and">
        <if test="name != null">
            and `name` like #{name}
        </if>
        <if test="state != 0">
            and state like #{state}
        </if>
    </trim>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

十二、MyBatis分页功能

MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。

1、pageHelper插件

在使用MyBatis分页功能时,通常和pageHelper插件配合使用

1.1 参数

PageInfo{
    
    
pageNum=1, 	//页数
pageSize=2, 	//页大小
size=2, 大小
startRow=1, 	//开始行
endRow=2, 	//结束行
total=6, 		//总条数
pages=3, 		//总页数
list=Page{
    
    count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=6, pages=3, reasonable=false, pageSizeZero=false}, 
prePage=0, 	//首页
nextPage=2, 	//下一页
isFirstPage=true, 	//是第一页
isLastPage=false,	// 是最后一页
hasPreviousPage=false, 	//有上一页
hasNextPage=true, 		//有下一页
navigatePages=2, 		//导航页数
navigateFirstPage=1, 	//导航到第一页
navigateLastPage=2,  	//导航到最后一页
navigatepageNums=[1, 2]}	//导航 页数 页码

1.2 使用步骤

  1. 导入依赖
<!--pageHelper-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.4</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<!-- 配置分页插件 -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 设置数据库类型-->
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>
  1. 接口
List<User> queryAllUser();
  1. Mapper

一定注意,不要在SQL语句后面加分号,否则MyBatis不能动态的正确拼接limt 分页语句

<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void queryAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 开启分页,pageNum表示当前页码,pageSize表示每页数据条数
    PageHelper.startPage(2,2);
    // 获取数据
    List<User> userList = mapper.queryAllUser();
    // 把数据封装在pageInfo对象中,后期可以通过这种方式返回给前端,进行分页展示
    PageInfo<User> userPageInfo = new PageInfo<>(userList);
    System.out.println(userPageInfo);
}

十三、log4j

​ 在使用SSM进行开发时, 随着代码量的增大, 配置信息的繁杂, 排错也随之变得越来越难. 而日志就是我们debug的有力工具,常用的日志有很多, 我们选择使用log4j,下文介绍log4j在MyBatis下的简单应用

1、使用步骤

  1. 导入依赖
<!--log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 创建 文件(直接在resource文件夹下创建)
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

十四、MyBatis缓存

​ 缓存可以将数据保存在内存中,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 开启二级缓存,但是并不是意味着我们之前写的所有sql都是二级缓存,要使用二级缓存需要在xml文件中使用<cache/>标签指出。

下面案例是演示配置了日志系统后,当你查询的条件一摸一样的时候只执行了一次sql。

1、一级缓存

  1. 一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
  2. 在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
  3. 由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

1.1 案例

  1. 保证日志开启,方便查看输出数据
  2. 接口
List<User> getUserList();
  1. Mapper
<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void getUserList() throws IOException {
    
    
        // 读取配置文件mybatis-config.xml
        InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
        // 根据配置文件构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
        // 通过SqlSessionFactory创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession执行文件中定义的SQL,并返回映射结果
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        System.out.println(userList);
        List<User> userList1 = mapper.getUserList();
        System.out.println(userList1);
        System.out.println(userList1==userList);// true
}

日志打印信息

[com.hqyj.cl.mapper.UserMapper.getUserList]-==>  Preparing: select * from `user` 
[com.hqyj.cl.mapper.UserMapper.getUserList]-==> Parameters: 
[com.hqyj.cl.mapper.UserMapper.getUserList]-<==      Total: 3
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]
===========================================================================================================
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]

1.2 缓存失效

  • 查询不同的东西,比如同一个SQL语句,但是传递的参数不同。
  • 查询不同的Mapper.xml。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存,也就是说,在两条相同语句中间插入一条其他语句(比如inset),缓存会刷新。
  • 手动清理缓存。sqlSession.clearCache()
  • 把第一个创建的sqlSession通过sqlSession.close()方法关闭后,再创建一个sqlSession,也无法实现缓存。

1.3 总结

一级缓存基本上没啥用,因为用户执行的操作出现一模一样的情况很少;通过debug可以发现,其实就是把查询出来的值存入Map集合。

2、二级缓存

二级缓存就是与namespace级别的缓存,一个名称空间对应一个缓存。

2.1 工作机制

​ 一个会话查询一条数据,这个数据会存放在当前会话的一级缓存中,当会话结束时,一级缓存就会消失,但是我们可以把当前会话数据保存在二级缓存中,新的会话查询信息就可以从二级缓存中获取内容,当然,不同的mapper查询的数据都会放在自己对应的缓存中。

2.2 格式

要启用全局的二级缓存,只需要在你的 SQL 映射文件中(XXXMapper.xml)添加一行<cache/>,比如

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

上述例子创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

配置详解

  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

2.3 可用的清除策略

  • LRU – 最近最少使用:移除最长时间不被使用的对象默认方式。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

2.4 注意事项

  • 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新;
  • 不能使用封装获取sqlSession对象的类;

2.5 案例

  1. 开启全局缓存(mybatis-config.xml)配置二级缓存后可以尝试不同的sqlsession执行方法,控制台只打印一条sql语句,证明

    是从缓存中取出数据

<!--开启全局缓存,默认也是开启的,但是我们一般显示定义-->
<setting name="cacheEnabled" value="true"/>

**注意事项:**在CRUD标签中,可以使用useCache属性指定是否启用二级缓存,默认true。

  1. 接口
List<User> getUserList();
  1. mapper
<!--开启二级缓存-->
<cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
  1. 测试
@Test
public void getUserList() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
    // 关闭第一个sqlSession连接,它会把缓存放入二级缓存中
    sqlSession.close();
    System.out.println("=======================================================");
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = mapper1.getUserList();
    System.out.println(userList1);
    System.out.println(userList1==userList);// true
    // 关闭第二个sqlSession连接
    sqlSession1.close();
}

2.6 错误及解决

  1. 错误

如果我们在mapper文件中,直接使用<cache/>方式开启二级缓存,会报错org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.hqyj.cl.pojo.User

  1. 解决

因为要使用二级缓存需要把对应的类序列化,所以解决办法如下

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    
    
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

十五、MyBatis逆向工程

Mybatis 提供了一个逆向工程工具,该工具可以根据数据表自动生成针对单表的 pojo 类、mapper 映射文件和 mapper 接口。

1、使用步骤

  1. 创建数据库
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `gender` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `role` varchar(255) NOT NULL DEFAULT '用户',
  `state` int(255) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
  1. 创建项目

新建项目,并且新建资源文件夹 config,在 config 文件夹下创建 generatorConfig.xml 文件,用于配置及指定数据库及表等。

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/servletdemo" userId="root"
                        password="112112" />

        <!-- 默认为false,把JDBC DECIMAL和NUMERIC类型解析为Integer;为true时 把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成POJO类的位置 -->
        <javaModelGenerator
                targetPackage="com.hqyj.cl.pojo" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.hqyj.cl.mapper"
                         targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- targetProject:mapper接口生成的的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.hqyj.cl.mapper" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <!-- 指定数据表 -->
        <table tableName="user"></table>
    </context>
</generatorConfiguration>

注意事项:targetProject写成绝对路径,否则可能出现路径不对,加载不出文件的情况

  1. 导入依赖
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.0</version>
</dependency>
  1. GeneratorSqlmap 类
package net.biancheng;

import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlMap {
    
    
    public void generator() throws Exception {
    
    
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        // 指定配置文件
        File configFile = new File("D:\\workspace\\mybatis\\mybatis_03\\src\\main\\resources\\config\\generator-config.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }

    // 执行main方法以生成代码
    public static void main(String[] args) {
    
    
        try {
    
    
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

注意事项:FILE文件路径写成绝对路径,相对路径可能存在路径不对的情况

2、说明

在 pojo 包中,有一部分是名字为 XxxExample 的类。类中包含以下 3 个成员变量

protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;;

以上变量说明如下:

  • distinct 字段用于指定 DISTINCT 查询。
  • orderByClause 字段用于指定 ORDER BY 条件,这个条件没有构造方法,直接通过传递字符串值指定。
  • oredCriteria 字段用于自定义查询条件。

Acho que você gosta

Origin blog.csdn.net/ailaohuyou211/article/details/130394313
Recomendado
Clasificación