Interpretação Detalhada da Estrutura de Camada de Persistência MyBatis: Treinamento Prático para Adicionar, Excluir, Modificar e Verificar Arquivos de Configuração

1. Introdução

Anteriormente, aprendemos o método de desenvolvimento nativo da estrutura de camada de persistência MyBatis e o método de desenvolvimento do agente Mapper, que resolveu os problemas de codificação rígida e operações complicadas ao usar o código básico JDBC para operar o banco de dados. Agora que o feedback trazido pelo artigo não é ruim, vamos usar o conteúdo aprendido anteriormente para fazer um treinamento de caso prático hoje.

Olhando para trás, por que usamos o MyBatis para desenvolver? Não é difícil entender que o MyBatis, como um excelente framework de camada de persistência, suporta sql personalizado, stored procedures e mapeamento avançado, eliminando quase todos os códigos JBDC e o trabalho de definição de parâmetros e obtenção de conjuntos de resultados. Ele resolve o problema de hard coding e operação incômoda do código Java ao usar o código básico JBDC para operar o banco de dados, conforme mostrado na figura:

O objetivo do treinamento do caso nesta seção: ser capaz de usar o arquivo de configuração para realizar a operação de adicionar, excluir, modificar e verificar.

2. Preparações

Hoje usamos MyBatis para completar a adição, exclusão, modificação e consulta de dados no banco de dados. A implementação específica é a seguinte:

  • Consultar dados
    • consultar todos os dados
    • Detalhes dos dados da consulta
    • consulta condicional
  • adicionando dados
  • mude os dados
    • Modifique todos os campos
    • Modificar campos dinâmicos
  • excluir dados
    • Exclusão de dados individuais
    • Exclusão de dados em massa

O caso de hoje recebe uma tabela de dados do aluno, incluindo ID do aluno, nome, sexo, notas e outras informações de campo, por meio do MyBatis para adicionar, excluir, modificar e consultar os dados no banco de dados. Devido ao tamanho do artigo, esta série de artigos está dividida em dois artigos. O primeiro artigo explica a operação de consulta e o último artigo é sobre adições, exclusões e alterações. Se você estiver interessado nesta parte do conteúdo, por favor passar para o próximo artigo.

Em primeiro lugar, precisamos criar uma tabela de banco de dados e adicionar dados. As instruções SQL envolvidas são as seguintes:

drop table if exists student;

create table student(
	id int primary key auto_increment,
	name varchar(10),
	gender char(1),
	score_english int,
	score_math int
);

insert into student(name,gender,score_english,score_math) values
('张三','男',61,65),
('李四','女',45,38),
('王五','男',85,53),
('小王','男',56,58),
('小樊','女',85,92);

A tabela de dados é a seguinte:

imagem-20230129193400530

Em seguida, crie a classe de entidade Student sob o org.chengzi.pojopacote :

public class Student{
    
    
    //id 主键
    private int id;
    //学生姓名
    private String name;
    //学生性别
    private String gender;
    //学生英语成绩
    private int scoreEnglish;
    //学生数学成绩
    private int scoreMath;
    
    //这里省略了Getter and Setter方法和重写的Object中的toString方法
}

Em seguida, escreva o caso de teste, escreva o código de teste de unidade em Teste aqui e crie a classe MyBatisTest no diretório de arquivo Java do código de teste. Como mostrado na imagem:

imagem-20230129194459753

Para melhorar a eficiência do desenvolvimento do MyBatis, instalamos o plug-in MyBatisX . Este plug-in tem duas funções principais. A primeira é o salto mútuo entre o arquivo de configuração de mapeamento XML e a interface do Mapper, e a segunda é automaticamente gere uma instrução de acordo com o método da interface Mapper, conforme a figura:

imagem-20230129194834069

Pesquise o plug-in MyBatisX em Arquivo/configuração/plug-ins para baixá-lo e instalá-lo.

A imagem azul representa o arquivo de interface do Mapper e o ícone vermelho representa o arquivo de configuração de mapeamento sql. Usando este plug-in, você pode definir métodos na interface do Mapper, gerar instruções automaticamente no arquivo de configuração e pular rapidamente para o Mapper correspondente interface no arquivo de configuração. Acho esse plug-in muito eficiente no desenvolvimento do MyBatis e o uso desde que aprendi o MyBatis.

3. Consulte todos os dados

Na página do cliente, geralmente precisamos exibir todas as informações de dados. Neste momento, o código subjacente é usar Java para operar o banco de dados, consultar todas as informações e enviá-las para a página do cliente.

Podemos consultar toda a informação através dos seguintes passos:

  • Escreva a interface do mapeador
    • Parâmetros: nenhum
    • resultado:List<Student>
  • Escrever arquivo de configuração de mapeamento sql
  • Escrever e executar código de teste

Ao analisar os métodos na interface do Mapper, é principalmente para analisar se os parâmetros e tipos de valor de retorno são necessários de acordo com os requisitos dos dados operacionais.

3.1 Métodos de interface de escrita

Crie uma interface StudentMapper no org.chengzi.mapperpacote e defina um método para consultar todos os dados dos alunos nesta interface:

public interface StudentMapper {
    
    
    /*
    需求:查询所有学生信息
     */
    List<Student> selectAll();

}

3.2 Escrever instrução sql

Crie uma estrutura de diretório sob o caminho org/chengzi/mapperde recursos e o separador de caminho deve ser usado /para garantir que a interface do mapeador e o arquivo de configuração de mapeamento sql correspondente estejam no mesmo diretório de arquivo. Crie o arquivo de configuração StudentMapper.xml neste diretório.

Depois de escrever o método correspondente na interface StudentMapper, através do ícone do plug-in MyBatisX, podemos pular rapidamente para o arquivo de configuração do mapeamento sql correspondente e a instrução é gerada automaticamente. Escreva uma instrução sql para consultar todas as informações do aluno no arquivo de configuração de mapeamento sql:

<?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="org.chengzi.mapper.StudentMapper">
    <select id="selectAll" resultType="student">
        select *
        from student;
    </select>
</mapper>

3.3 Métodos de teste de redação

Na classe MyBatisTest, escreva o código para testar e consultar todas as informações do aluno, da seguinte forma:

public class MyBatisTest {
    
    
    @Test
    public void testSelectAll() throws IOException {
    
    
        //1. 获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3. 获取Mapper接口的代理对象
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        //4. 执行方法
        List<Student> students = studentMapper.selectAll();
        System.out.println(students);

        //5. 释放资源
        sqlSession.close();
    }
}

Depois de aprender a estrutura do SSM no futuro, essa parte do código se tornará muito simples. O foco aqui está na linha de código que executa o método.

resultado da operação:

imagem-20230129202650613

Verifica-se aqui que as informações de alguns campos do banco de dados não estão encapsuladas no objeto Java. O motivo do problema é muito simples, pois os nomes das colunas das tabelas do banco de dados são inconsistentes com os nomes dos atributos das classes da entidade no código Java. Por exemplo, pontuações de matemática dos alunos, em MySQL O método chamado score_math é usado em Java, mas é denominado scoreMath em Java e não há problema com os dois métodos de nomenclatura.

Existem duas maneiras de resolver este problema :

  1. Nomes de campo da tabela de dados com alias
  2. Use resultMap para definir o relacionamento de mapeamento entre nomes de campos e nomes de atributos

Por exemplo:

<mapper namespace="org.chengzi.mapper.StudentMapper">
    <select id="selectAll" resultType="student">
        select id,name,gender,score_english as scoreEnglish,score_Math as scoreMath
        from student;
    </select>
</mapper>

Neste ponto, o problema de que os dados não podem ser encapsulados em objetos foi resolvido, mas ao consultar todas as informações dos alunos, precisamos listar todos os nomes dos campos, o que obviamente é ineficiente e desaconselhável. MyBatis fornece fragmentos SQL para resolver este problema.

Por exemplo:

<mapper namespace="org.chengzi.mapper.StudentMapper">
    <!--
	sql片段
	-->
    <sql id="student_column">
        select id,name,gender,score_english as scoreEnglish,score_Math as scoreMath
    </sql>
    
    <select id="selectAll" resultType="student">
        <include refid="student_column"/>
        from student;
    </select>
</mapper>

O id é usado como o identificador único do fragmento sql, e basta usar a <include>referência do rótulo na instrução sql original ao usá-lo.

Este é outro problema. Se você operar em alguns campos da tabela de dados, haverá um grande número de fragmentos sql, o que obviamente não é aconselhável. Todos os MyBatis usam o método resultMap para resolver este problema.

3.4 Uso do resultMap

Ao resolver o problema de que alguns campos na tabela de dados não podem ser encapsulados em objetos Java, o método de uso de aliases é ineficiente e o método de fragmentos sql não é flexível. MyBatis fornece o método resultMap para definir o relacionamento de mapeamento entre nomes de campo e nomes de atributos.

Ao usar, você só precisa usar o seguinte método para definir no arquivo de configuração de mapeamento sql:

<mapper namespace="org.chengzi.mapper.StudentMapper">

    <resultMap id="studentResultMap" type="student">
        <result column="score_english" property="scoreEnglish"/>
        <result column="score_math" property="scoreMath"/>
    </resultMap>
    
    <select id="selectAll" resultMap="studentResultMap">
     select *
        from student;
    </select>
</mapper>

Ao utilizar este método, é necessário apenas mapear a parte do nome do campo na tabela de dados que é diferente do atributo na classe de entidade Java, o que melhora muito a eficiência. O id na tag resultMap é usado como um identificador exclusivo e usado na instrução.

No resultMap, há duas tags disponíveis, para mapeamento para dados gerais e para mapeamento para dados de chave primária.

Execute o programa:

imagem-20230129210333422

Os dados na tabela de dados foram todos encapsulados em objetos Java.

4. Detalhes da consulta

No cliente, muitas vezes os dados não são exibidos na íntegra, mas em parte, e a outra parte geralmente precisa ser visualizada na forma de visualização de detalhes. Neste momento, quando o usuário seleciona o aluno especificado e verifica as informações, o id do aluno é enviado para o código Java, e todas as informações do aluno são consultadas através do id do usuário.

Podemos implementar a função de detalhes da consulta através das seguintes etapas:

  • Escreva a interface do mapeador:
    • Parâmetro: id
    • Valor de retorno: Aluno
  • Escrever arquivo de mapeamento sql
  • Execute o método de teste

Na consulta de detalhes, muitas vezes é necessário passar um parâmetro, como id, para consultar todas as informações do aluno de acordo com o id, e o resultado retornado é apenas um dado de registro, então só precisa ser encapsulado em um objeto.

4.1 Métodos de interface de escrita

Defina o método de consulta de dados com base no id na interface StudentMapper:

/**
  * 查看详情:根据Id查询
  */
Student selectById(int id);

4.2 Escrever instrução sql

Através da função de salto rápido do MyBatisX, pule para o arquivo de configuração de mapeamento sql correspondente. Neste momento, a instrução foi gerada automaticamente e o resultMap definido anteriormente pode ser usado diretamente.

<select id="selectById"  resultMap="studentResultMap">
    select *
    from student where id = #{id};
</select>

O acima é #{id}um marcador de posição de parâmetro, semelhante ao usado antes , que será explicado posteriormente.

4.3 Métodos de teste de redação

Na classe MyBatisTest, escreva o código para testar e consultar todas as informações do aluno, da seguinte forma:

@Test
    public void testSelectById() throws IOException {
    
    
        //接收参数
        int id =2;
        //1. 获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3. 获取Mapper接口的代理对象
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        //4. 执行方法
        Student students = studentMapper.selectById(id);
        System.out.println(students);

        //5. 释放资源
        sqlSession.close();
    }

Neste ponto, ao contrário da consulta anterior de todas as informações do aluno, os parâmetros e valores de retorno deste método são diferentes. Precisamos apenas passar os parâmetros recebidos pelo código Java, e então podemos encapsular os dados consultados em um objeto da classe Aluno.

resultado da operação:

imagem-20230129212902381

4.4 Espaços reservados para parâmetros

Como mencionado anteriormente, a instrução sql é usada #{xx}como um espaço reservado para o parâmetro, e você pode ver no log em execução que, de fato, Java substitui automaticamente a posição do espaço reservado para o parâmetro durante o processo em execução , o que resolve a injeção de sql causada pela ortografia da questão das strings sql . Como mostrado na imagem:

imagem-20230129213409241

Na verdade, o espaço reservado em MyBatis #{xx}também ${xx}, o que é fácil de causar problemas de injeção de sql ao soletrar strings, portanto, geralmente é usado após o from da instrução sql quando o nome da tabela da consulta é incerto.

Não é difícil descobrir que a diferença entre os dois é que #{xx}a camada inferior do espaço reservado é o preparadoStatement, enquanto ${xx}a é a instrução, que tem o problema de injeção de sql. Portanto, é recomendável usar o primeiro como um espaço reservado de parâmetro no desenvolvimento do MyBatis.

4.5 uso do parameterType

Se houver parâmetros no método na interface do Mapper, o parameterType deve ser configurado no arquivo de mapeamento sql correspondente para especificar o tipo de dados do parâmetro, mas esse atributo pode ser omitido. Não é difícil entender que a razão pela qual pode ser omitido é porque o tipo de dado deste parâmetro foi definido no arquivo de interface do Mapper.

do seguinte modo:

<mapper>   
	<select id="selectById" parameterType="int" resultMap="studentResultMap">
select * from student where id=#{id};
    </select>
</mapper>

4.6 Manipulação de caracteres especiais

Quando o MyBatis escrever sql no arquivo de configuração do mapeamento sql correspondente, alguns caracteres especiais aparecerão, por exemplo, quando o sinal de menor for usado, será confundido com a <parte do . Como mostrado na imagem:

imagem-20230129233235106

MyBatis também fornece um método correspondente para resolver este problema, você pode usar estes dois métodos:

  1. Caractere de escape: usado quando há poucos caracteres especiais
  2. CDATE: Use quando houver muitos caracteres especiais confusos

Por exemplo, usando caracteres de escape:

imagem-20230129234459723

Ou use o método CDATA:

imagem-20230129234642325

Ao usar um IDE com a função de preenchimento automático de código, apenas uma parte da entrada é necessária para o preenchimento automático. Este método é usado quando há muitos caracteres especiais.

5. Consulta multicondição

Na operação real do cliente, frequentemente consultamos dados com base na satisfação simultânea de várias condições, como consultar as informações de todos os alunos cujas pontuações em inglês e matemática são maiores que 60 no caso de consulta.

O foco aqui é como escrever instruções SQL.

Podemos implementar a função de detalhes da consulta através das seguintes etapas:

  • Escreva a interface do mapeador
    • Parâmetros: todas as condições de consulta
    • resultado:List<Student>
  • Escrever arquivo de configuração de mapeamento sql
  • Execute o método de teste

5.1 Métodos de interface de escrita

Para definir o método de consulta multicondição na interface StudentMapper, os parâmetros também devem ser definidos ao definir a interface.MyBatis possui vários métodos de implementação para vários parâmetros de consulta multicondição.

Método um :

Use para @Param("参数名称")marcar cada parâmetro, que precisa ser usado no arquivo de configuração de mapeamento como #{参数名称}um espaço reservado

List<Student> selectByCondition(@Param("scoreEnglish") int scoreEnglish, @Param("scoreMath") int scoreMath);

Método dois :

Encapsule vários parâmetros em um objeto de entidade e use o objeto de entidade como o parâmetro de método da interface. Este método requer que, #{内容}quando , o conteúdo dentro dele seja consistente com o nome do atributo da classe de entidade.

List<Student> selectByCondition(Student student);

Método três :

Encapsule vários parâmetros na coleção de mapas e use a coleção de mapas como o parâmetro de método da interface. Este método requer que, #{内容}quando , o conteúdo interno seja consistente com o nome da chave na coleção de mapas.

List<Student> selectByCondition(Map map);

5.2 Escrever instrução sql

Escreva a instrução correspondente no arquivo StudentMapper.xml, onde resultMap também é usado. Exemplo:

<mapper> 
	<select id="selectByCondition" resultMap="studentResultMap">
        select * from student 
                 where score_english > #{scoreEnglish} and score_math > #{scoreMath};
    </select>
</mapper>

5.3 Métodos de teste de redação

Na consulta multicondição, como existem três métodos diferentes de definição de parâmetros ao definir a interface do Mapper, os métodos de execução específicos aqui também são divididos em três métodos diferentes.

    @Test
    public void testSelectByCondition() throws IOException {
    
    
        //接收参数
        int scoreEnglish=60;
        int scoreMath=60;
        //1. 获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3. 获取Mapper接口的代理对象
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        //4. 执行方法
        //方法1.使用第一种方法定义参数时使用
        List<Student> students = studentMapper.selectByCondition(scoreEnglish,scoreMath);
        System.out.println(students);
        
        //方法2.使用第二种方法定义参数时使用
        Student student = new Student();
        student.setScoreEnglish(scoreEnglish);
        student.setScoreMath(scoreMath);

        List<Student> students = studentMapper.selectByCondition(student);
        System.out.println(students);
        
        //方法3.使用第三种方法定义参数时使用
        Map map = new HashMap();
        map.put("scoreEnglish" , scoreEnglish);
        map.put("scoreMath" , scoreMath);
        
        List<Student> students = studentMapper.selectByCondition(map);
        System.out.println(students);
        //5. 释放资源
        sqlSession.close();
    }
}

Usando três métodos para executar o programa, os resultados são os mesmos, conforme mostrado na figura:

imagem-20230129224312015

5.4 SQL dinâmico

Acima, definimos três parâmetros na consulta multicondição e passamos três parâmetros ao executar o programa de teste. No entanto, a realidade é que o usuário pode não inserir um valor para cada parâmetro e, neste momento, ocorre um problema com o sql acima.

Quando o usuário insere dois parâmetros, a instrução SQL é a seguinte:

select * from student where score_english > #{scoreEnglish} and score_math > #{scoreMath};

Quando o usuário insere apenas uma condição, a instrução sql é a seguinte:

select * from student where score_english > #{scoreEnglish} ;

Para este problema, MyBatis tem uma solução poderosa:

  • E se
  • escolher (quando, caso contrário)
  • trim(onde,definir)
  • para cada

Por exemplo, use o seguinte método para resolver:

<select id="selectByCondition" resultMap="studentResultMap">
     select * from student 
     where
        <if test="scoreEnglish !=null">
              score_english > #{scoreEnglish}
        </if>
		<if test="scoreMath !=null">
              and score_math > #{scoreMath}
        </if>

</select>

Usando este método, as strings serão emendadas dinamicamente quando o programa for executado. Se ambos os dados forem passados, a emenda da instrução sql é a seguinte:

imagem-20230129231539021

E se o usuário não passar o último valor, então o programa também é executado normalmente, a string sql neste momento:

imagem-20230129231659101

Mas se o primeiro não tiver dados de entrada e apenas o segundo tiver dado dados, o programa terá um erro, pois ao unir as strings sql, aparecerá um e depois de where, e o erro de sintaxe sql ocorrerá neste momento. do seguinte modo:

select * from student where and scoreMath > ? ;//语法错误

Este problema pode ser resolvido usando a tag where, que pode substituir a palavra-chave where, e o e depois da primeira condição será removido dinamicamente.Se todos os parâmetros não tiverem um valor determinado, a palavra-chave where não será usada.

Observação: neste ponto, você precisa adicionar a palavra-chave and a cada condição, e o e <where>após a primeira condição será removido dinamicamente.

Neste ponto, o programa é executado com sucesso e satisfaz o problema de que o usuário pode não inserir valores para todos os parâmetros. Como mostrado na imagem:

imagem-20230129232606376

6. Consulta de condição única de SQL dinâmico

No cliente, às vezes o usuário pode escolher uma consulta condicional, mas o código Java não sabe qual condição escolher, então é necessário usar a consulta condicional única do sql dinâmico.

Esse requisito pode ser implementado usando a choose(when,otherwise)tag , e o uso da tag select é semelhante à instrução Switch em Java.

6.1 Métodos de interface de escrita

O método de consulta de condição única para escrever sql dinâmico na interface Mapper:

List<Student> selectByConditionSingle(Map map);

6.2 Escrever instrução sql

Escreva instruções sql no arquivo de configuração de mapeamento sql, como segue:

<mapper>    
    <select id="selectByConditionSingle" resultMap="studentResultMap">

        select * from student
        <where>
            <choose>
                <when test="scoreEnglish !=null">
                    score_english > #{scoreEnglish}
                </when>
                <when test="scoreMath !=null">
                    score_math > #{scoreMath}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
</mapper>

6.3 Métodos de teste de redação

Escreva o código do teste de unidade na classe MyBatisTest, da seguinte maneira:

@Test
    public void testSelectByConditionSingle() throws IOException {
    
    
        //接收参数
        int scoreEnglish=60;
        int scoreMath=60;
        //1. 获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3. 获取Mapper接口的代理对象
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        Map map = new HashMap();
        map.put("scoreEnglish" , scoreEnglish);
        //map.put("scoreMath" , scoreMath);

        List<Student> students = studentMapper.selectByConditionSingle(map);
        System.out.println(students);
        //5. 释放资源
        sqlSession.close();
    }

O resultado da execução está correto, conforme mostrado na figura:

imagem-20230130013053973

7. Resumo

Este artigo é um exercício prático de desenvolvimento do MyBatis e uso de arquivos de configuração para implementar operações de adição, exclusão, modificação e consulta. Devido à extensão do artigo, este artigo envolve apenas operações de consulta. Como o método mais comum de operação de bancos de dados, operações de consulta devem ser praticadas continuamente para se tornar proficiente. Ao escrever SQL, embora a capitalização não seja enfatizada, é recomendável usar maiúsculas, porque a capitalização é um método SQL mais padronizado.

Terminei de escrever, verifiquei a hora, agora são duas horas da manhã, acabei de escrever este artigo e estava extraordinariamente quieto lá fora. A criação não é fácil, espero que possa ajudá-lo. Curta primeiro, assista depois, torne isso um hábito e até a próxima edição.

imagem-20230130001121015

Acho que você gosta

Origin blog.csdn.net/zhangxia_/article/details/128796222
Recomendado
Clasificación