【免费Web系列】JavaWeb实战项目案例二

 

   这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 

前言

在前面我们学习MySQL数据库时,使用图形化客户端工具(如:idea、MySQL Workbench),来操作数据库的。

在客户端工具中,编写增删改查的SQL语句,发给MySQL数据库管理系统,由数据库管理系统执行SQL语句并返回执行结果。

增删改操作:返回受影响行数

查询操作:返回结果集(查询的结果)

我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库的技术呢,有很多啊。

  • JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。 【是操作数据库最为基础、底层的技术】

那现在在企业项目开发中呢,一般都会使用基于JDBC的封装的高级框架,比如:Mybatis、MybatisPlus、Hibernate、SpringDataJPA。 而这些技术,目前的市场占有份额如下图所示:

从上图中,我们也可以看到,目前最为主流的就是Mybatis,其次是MybatisPlus。

所以,在我们的课程体系中呢,这两种主流的操作数据库的框架我们都要学习。 而我们在学习这两个主流的框架之前,还需要学习一下操作数据库的基础基础 JDBC。 然后接下来,再来学习Mybatis。 而在我们后面的课程中,我们还要学习MybatisPlus框架。 那么今天呢,我们就先来学习 JDBC 和 Mybatis。

我们今天的课程安排如下:

  • JDBC

  • Mybatis基础

  • 部门列表查询

1. JDBC

1.1 概述

JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。

本质:

  • sun公司官方定义的一套操作所有关系型数据库的规范,即接口。

  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包。

  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

1.2 快速入门

需求:通过JDBC程序,执行update语法,更新用户表中的数据

步骤:

  • 准备工作:

    • 创建项目,引入mysql的驱动、junit依赖

    • 注册驱动

    • 获取连接对象 Connection

    • 获取SQL语句执行对象 Statement

  • 执行SQL语句

  • 释放资源

演示:

  1. 准备数据库 web,及数据库表 user

    create database web;
    use web;
    ​
    create table user(
        id int unsigned primary key auto_increment comment 'ID,主键',
        username varchar(20) comment '用户名',
        password varchar(32) comment '密码',
        name varchar(10) comment '姓名',
        age tinyint unsigned comment '年龄'
    ) comment '用户表';
    ​
    insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),
                                                               (2, 'xiaoqiao', '123456', '小乔', 18),
                                                               (3, 'diaochan', '123456', '貂蝉', 24),
                                                               (4, 'lvbu', '123456', '吕布', 28),
                                                               (5, 'zhaoyun', '12345678', '赵云', 27);

  1. 创建一个普通的maven项目(非springboot项目),引入mysql的驱动、junit依赖

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    ​
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.3</version>
        <scope>test</scope>
    </dependency>

  1. src/test 目录下创建一个包 com.itheima.test,并在其中创建一个测试类 JDBCTest,编写入门程序 ,具体如下:

    @Test
    public void testUpdate() throws Exception {
        //1.准备工作
        //1.1 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        
        //1.2 获取连接
        String url = "jdbc:mysql://localhost:3306/web";
        Connection connection = DriverManager.getConnection(url, "root", "1234");
        
        //1.3 获取SQL语句执行对象
        Statement statement = connection.createStatement();
    ​
        //2.执行SQL
        statement.executeUpdate("update user set password = '1234567890' where id = 1");
    ​
        //3.释放资源
        statement.close();
        connection.close();
    }

在上述的步骤中,注册驱动、获取链接Connection、获取SQL语句执行对象 Statement 、释放资源 这几步都是固定步骤,使用JDBC程序操作数据库时,这几步都需要做的。

  1. 运行单元测试,查看数据库中数据的执行结果。

1.3 API详解

1.3.1 DriverManager

作用:

  • 注册驱动

  • 获取数据库链接 Connection

1.3.1.1 注册驱动

而通过上述的代码,大家可以看到注册驱动,我们是通过 Class.forName("com.mysql.cj.jdbc.Driver") 来注册的,看似和DriverManager没什么联系。其实不然,Class.forName("com.mysql.cj.jdbc.Driver") 只是将 Driver 这个类加载到JVM中。 而在 Driver 这个类中定义了静态代码块,内容如下:

Driver 这个类被加载时,就会自动执行静态代码块中的代码,然后就完成了注册驱动的操作。

备注:

而其实啊,Class.forName("com.mysql.cj.jdbc.Driver") 这句代码呢,是可以省略的,省略了,也可以正常的完成驱动的注册。 原因是因为在MySQL的驱动jar包中,在 META-INF/services 目录中提供了一个文件 java.sql.Driver ,在这个文件中编写了一行内容,就是驱动类的全类名 :

当在程序需要用到这个类时,java会自动加载这个类,这个类一加载 就会自动执行静态代码块中的内容,就完成了注册驱动的操作 ,而java中的这个机制称之为 SPI

SPI机制:Service Provider Interface,JDK内置的一种服务提供发现机制,可以轻松的扩展你得程序(切换实现),实现接口与实现类之间的解耦。

1.3.1.2 获取链接

DriverManager.getConnection(url, username, password);

  • url: 数据库连接url

    • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名?参数键值对1&参数键值对2

    • 说明:如果连接的是本机的默认端口的mysql,url可以简写为:jdbc:mysql:///数据库名?参数键值对…

  • user:用户名

  • password:密码

1.3.2 Connection & Statement

  • Connection的作用:获取执行SQL的对象

    • 执行普通SQL对象的Statement:connection.createStatement();

    • 执行预编译SQL对象PreparedStatement:connection.preparedStatement(); 【后面单独讲解】

  • Statement的作用:执行SQL

    • 执行DDL、DML语句:executeUpdate(sql); 如果是执行DML语句完毕,返回值int代表 DML 语句影响的函数 。

    • 执行SQL语句:executeQuery(sql); 返回值为ResultSet,里面封装了查询结果。

1.3.3 ResultSet

  • 需求:请根据 用户名 与 密码 查询用户的信息,来模拟用户登录操作。

  • 实现如下:

    @Test
    public void testQuery() throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
    ​
        //2. 获取链接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web01", "root", "root@1234");
    ​
        //3. 获取数据库执行对象 Statement
        Statement statement = connection.createStatement();
    ​
        //4. 执行SQL
        ResultSet resultSet = statement.executeQuery("select * from user where username = 'lvbu' and password = '123456'");
    ​
        //5. 获取结果
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            System.out.println(new User(id,username,password,name,age));
        }
    ​
        //6. 释放资源
        statement.close();
        connection.close();
    }

  • ResultSet(结果集对象):封装了DQL查询语句查询的结果。

    • next():将光标从当前位置向前移动一行,并判断当前行是否为有效行,返回值为boolean。

      • true:有效行,当前行有数据

      • false:无效行,当前行没有数据

    • getXxx(…):获取数据,可以根据列的编号获取,也可以根据列名获取(推荐)。

而上述的单元测试中,我们在SQL语句中,将将 用户名 和密码的值都写死了,而这两个值应该是动态的,是将来页面传递到服务端的。 那么,我们可以基于前面所讲解的JUnit中的参数化测试进行单元测试,代码改造如下:

   
 /**
     * 根据用户名和密码查询用户的基本信息 - 参数化测试
     */
    @ParameterizedTest
    @CsvSource({"lvbu,123456", "xiaoqiao,123456"})
    public void testQuery2(String _username , String _password) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
​
        //2. 获取链接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web01", "root", "root@1234");
​
        //3. 获取数据库执行对象 Statement
        Statement statement = connection.createStatement();
​
        //4. 执行SQL
        ResultSet resultSet = statement.executeQuery("select * from user where username = '"+_username+"' and password = '"+_password+"'");
​
        //5. 获取结果
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            System.out.println(new User(id,username,password,name,age));
        }
​
        //6. 释放资源
        statement.close();
        connection.close();
    }

如果在测试时,需要传递一组参数,可以使用 @CsvSource 注解。

1.3.4 PreparedStatement

  • 作用:预编译SQL语句并执行,可以防止SQL注入问题。

  • SQL注入:通过控制输入来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

1.3.4.1 SQL注入演示

1). 打开课程资料中的文件夹 资料/02. SQL注入演示,运行其中的jar包 sql_Injection_demo-0.0.1-SNAPSHOT.jar,进入该目录后,执行命令:

java -jar sql_Injection_demo-0.0.1-SNAPSHOT.jar

2). 打开浏览器访问 http://localhost:9090/ ,必须登录后才能访问到系统。我们先测试正常的用户名和密码

3). 接下来,我们再来测试一下错误的用户名和密码 。

我们看到,如果用户名密码错误,是不能进入到系统中进行访问的,会提示 用户名和密码错误

4). 那接下来,我们就要演示一下SQL注入现象,我们可以通过控制表单输入,来修改事先定义好的SQL语句的含义。 从而来攻击服务器。

点击登录后,我们看到居然可以成功进入到系统中。

为什么会出现这种现象呢?

在进行登录操作时,怎么样才算登录成功呢? 如果我们查询到了数据,就说明用户名密码是对的。 如果没有查询到数据,就说明用户名或密码错误。

而出现上述现象,原因就是因为,我们我们编写的SQL语句是基于字符串进行拼接的 。 我们输入的用户名无所谓,比如:shfhsjfhja ,而密码呢,就是我们精心设计的,如:' or '1' = '1

那最终拼接的SQL语句,如下所示:

我们知道,or 连接的条件,是或的关系,两者满足其一就可以。 所以,虽然用户名密码输入错误,也是可以查询返回结果的。

1.3.4.2 SQL注入解决

而我们这小节,所讲解的 PreparedStatement 就可以解决SQL注入的问题。那么接下来,我们就来演示一下 PreparedStatement 的基本使用:

/**
 * 根据用户名和密码查询用户的基本信息 - PreparedStatement
 */
@ParameterizedTest
@CsvSource({"lvbu,123456"})
public void testQuery3(String _username , String _password) throws Exception {
    //1. 注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
​
    //2. 获取链接
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web01", "root", "root@1234");
​
    //3. 获取数据库执行对象 Statement
    PreparedStatement preparedStatement = connection.prepareStatement("select * from user where username = ? and password = ?");
    preparedStatement.setString(1, _username);
    preparedStatement.setString(2, _password);
​
    //4. 执行SQL
    ResultSet resultSet = preparedStatement.executeQuery();
​
    //5. 获取结果
    while (resultSet.next()){
        int id = resultSet.getInt("id");
        String username = resultSet.getString("username");
        String password = resultSet.getString("password");
        String name = resultSet.getString("name");
        int age = resultSet.getInt("age");
        System.out.println(new User(id,username,password,name,age));
    }
​
    //6. 释放资源
    preparedStatement.close();
    connection.close();
}

select * from user where username = ? and password = ? 这种SQL呢,我们称之为预编译SQL 。那么基于这种预编译SQL呢,是可以解决SQL注入问题的 。

1). 打开课程资料中的文件夹 资料/02. SQL注入演示,运行其中的jar包 sql_prepared_demo-0.0.1-SNAPSHOT.jar,进入该目录后,执行命令:

java -jar sql_prepared_demo-0.0.1-SNAPSHOT.jar

2). 打开浏览器访问 http://localhost:9090/ ,必须登录后才能访问到系统 。我们先测试正常的用户名和密码

3). 那接下来,我们就要演示一下是否可以基于上述的密码 ' or '1' = '1,来完成SQL注入 。

通过控制台,可以看到输入的SQL语句,是预编译SQL语句。

而在预编译SQL语句中,当我们执行的时候,会把整个' or '1'='1作为一个完整的参数,赋值给第2个问号(' or '1'='1进行了转义,只当做字符串使用)

那么此时再查询时,就查询不到对应的数据了,登录失败。

注意:在以后的项目开发中,我们使用的基本全部都是预编译SQL语句。

预编译SQL的优势:

  • 安全(防止SQL注入)

  • 性能更高

2. Mybatis基础

2.1 介绍

什么是MyBatis?

  • MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。

  • MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

  • 官网:https://mybatis.org/mybatis-3/zh/index.html

在上面我们提到了两个词:一个是持久层,另一个是框架。

  • 持久层:指的是就是数据访问层(dao),是用来操作数据库的。

  • 框架:是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础上进行软件开发更加高效、规范、通用、可拓展。

通过Mybatis就可以大大简化原生的JDBC程序的代码编写,比如 通过 select * from user 查询所有的用户数据,通过JDBC程序操作呢,需要大量的代码实现,而如果通过Mybatis实现相同的功能,只需要简单的三四行就可以搞定。

2.2 快速入门

需求:使用Mybatis查询所有用户数据 。

2.2.1 准备工作

2.2.1.1 创建springboot工程

创建springboot工程,并导入 mybatis的起步依赖、mysql的驱动包、lombok。

项目工程创建完成后,自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖

<dependencies>
    <!-- Mybatis的起步依赖 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
​
    <!-- lombok驱动 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- 单元测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.2.1.2 数据准备

创建用户表user,并创建对应的实体类User。

  • 用户表(如果已经存在,就不用创建了)

create table user(
    id int unsigned primary key auto_increment comment 'ID,主键',
    username varchar(20) comment '用户名',
    password varchar(32) comment '密码',
    name varchar(10) comment '姓名',
    age tinyint unsigned comment '年龄'
) comment '用户表';
​
insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),
                                                           (2, 'xiaoqiao', '123456', '小乔', 18),
                                                           (3, 'diaochan', '123456', '貂蝉', 24),
                                                           (4, 'lvbu', '123456', '吕布', 28),
                                                           (5, 'zhaoyun', '12345678', '赵云', 27);

  • 实体类:实体类的属性名与表中的字段名一一对应。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id; //ID
    private String username; //用户名
    private String password; //密码
    private String name; //姓名
    private Integer age; //年龄
}

2.2.1.3 配置Mybatis

application.properties 中配置数据库的连接信息。

spring.datasource.url=jdbc:mysql://localhost:3306/web01
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root@1234

上述的配置,可以直接复制过去,不要敲错了。 全部都是 spring.datasource.xxxx 开头。

2.2.2 编写SQL语句

在创建出来的springboot工程中,在引导类所在包下,在创建一个包 mapper 。在 mapper 包下创建一个接口 UserMapper ,这是一个持久层接口(Mybatis的持久层接口规范一般都叫 XxxMapper)。

UserMapper的内容如下:

import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
​
@Mapper
public interface UserMapper {
    /**
     * 查询全部
     */
    @Select("select * from user")
    public List<User> findAll();
}

@Mapper注解:表示是mybatis中的Mapper接口

  • 程序运行时:框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理

@Select注解:代表的就是select查询,用于书写select查询语句

2.2.3 单元测试

在创建出来的SpringBoot工程中,在src下的test目录下,已经自动帮我们创建好了测试类 ,并且在测试类上已经添加了注解 @SpringBootTest,代表该测试类已经与SpringBoot整合。

该测试类在运行时,会自动通过引导类加载Spring的环境(IOC容器)。我们要测试那个bean对象,就可以直接通过@Autowired注解直接将其注入进行,然后就可以测试了。

测试类代码如下:

@SpringBootTest
public class MybatisTest {
​
    @Autowired
    private UserMapper userMapper;
​
    @Test
    public void testFindAll(){
        List<User> userList = userMapper.findAll();
        userList.forEach(user -> {
            System.out.println(user);
        });
    }
​
}

运行结果:

User(id=1, username=daqiao, password=1234567890, name=大乔, age=22)
User(id=2, username=xiaoqiao, password=123456, name=小乔, age=18)
User(id=3, username=diaochan, password=123456, name=貂蝉, age=24)
User(id=4, username=lvbu, password=123456, name=吕布, age=28)
User(id=5, username=zhaoyun, password=12345678, name=赵云, age=27)

<font color='red'> 注意:测试类所在包,需要与引导类所在包相同。</font>

2.2.4 配置Mybatis的日志输出

默认情况下,在Mybatis中,SQL语句执行时,我们并看不到SQL语句的执行日志。 加入如下配置,即可查看日志:

#mybatis的配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

打开上述开关之后,再次运行单元测试,就可以看到控制台输出的SQL语句是什么样子的。

2.4 JDBC VS Mybatis

JDBC程序的缺点:

  • url、username、password 等相关参数全部硬编码在java代码中。

  • 查询结果的解析、封装比较繁琐。

  • 每一次操作数据库之前,先获取连接,操作完毕之后,关闭连接。 频繁的获取连接、释放连接造成资源浪费。

分析了JDBC的缺点之后,我们再来看一下在mybatis中,是如何解决这些问题的:

  1. 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.properties中

  2. 查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注

  3. 在mybatis中使用了数据库连接池技术,从而避免了频繁的创建连接、销毁连接而带来的资源浪费。

使用SpringBoot+Mybatis的方式操作数据库,能够提升开发效率、降低资源浪费

而对于Mybatis来说,我们在开发持久层程序操作数据库时,需要重点关注以下两个方面:

  1. application.properties

    #驱动类名称
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    #数据库连接的url
    spring.datasource.url=jdbc:mysql://localhost:3306/web01
    #连接数据库的用户名
    spring.datasource.username=root
    #连接数据库的密码
    spring.datasource.password=1234

  2. Mapper接口(编写SQL语句)

    @Mapper
    public interface UserMapper {
        @Select("select * from user")
        public List<User> list();
    }

2.5 数据库连接池

在前面我们所讲解的mybatis中,使用了数据库连接池技术,避免频繁的创建连接、销毁连接而带来的资源浪费。

下面我们就具体的了解下数据库连接池。

2.5.1 介绍

没有使用数据库连接池:

  • 客户端执行SQL语句:要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行后又需要关闭连接对象从而释放资源,每次执行SQL时都需要创建连接、销毁链接,这种频繁的重复创建销毁的过程是比较耗费计算机的性能。

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

  • 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象

允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

  • 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)

释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏

  • 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象

数据库连接池的好处:

  1. 资源重用

  2. 提升系统响应速度

  3. 避免数据库连接遗漏

2.5.2 产品

要怎么样实现数据库连接池呢?

  • 官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口)

    • 功能:获取连接

      public Connection getConnection() throws SQLException;
    • 第三方组织必须按照DataSource接口实现

常见的数据库连接池:

  • C3P0

  • DBCP

  • Druid

  • Hikari (springboot默认)

现在使用更多的是:Hikari、Druid (性能更优越)

1). Hikari(追光者) [默认的连接池]

2). Druid(德鲁伊)

  • Druid连接池是阿里巴巴开源的数据库连接池项目

  • 功能强大,性能优秀,是Java语言最好的数据库连接池之一

如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要完成以下两步操作即可:

参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

  1. 在pom.xml文件中引入依赖

<dependency>
    <!-- Druid连接池依赖 -->
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.19</version>
</dependency>
  1. 在application.properties中引入数据库连接配置

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.druid.username=root
spring.datasource.druid.password=1234

2.6 Mybatis的XML配置文件

Mybatis的开发有两种方式:

  1. 注解

  2. XML

2.6.1 XML配置文件规范

使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。

在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)

  2. XML映射文件的namespace属性为Mapper接口全限定名一致

  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

<select>标签:就是用于编写select查询语句的。

  • resultType属性,指的是查询返回的单条记录所封装的类型。

2.6.2 XML配置文件实现

第1步:创建XML映射文件

第2步:编写XML映射文件

xml映射文件中的dtd约束,直接从mybatis官网复制即可

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
 
</mapper>

第3步:配置

a. XML映射文件的namespace属性为Mapper接口全限定名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
​
</mapper>

b. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
​
    <!--查询操作-->
    <select id="findAll" resultType="com.itheima.pojo.User">
        select * from user
    </select>
</mapper>

运行测试类,执行结果:

2.6.3 MybatisX的使用

MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。

MybatisX的安装:

可以通过MybatisX快速定位:

MybatisX的使用在后续学习中会继续分享

学习了Mybatis中XML配置文件的开发方式了,大家可能会存在一个疑问:到底是使用注解方式开发还是使用XML方式开发?

官方说明:入门_MyBatis中文网

结论:使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。

3. 部门列表查询

3.1 功能实现

3.1.1 需求

查询数据库表中的所有部门数据,展示在页面上。

3.1.2 实现

  1. 准备数据库表 dept 及 实体类 Dept

    -- 部门管理
    create table dept(
        id int unsigned primary key auto_increment comment '主键ID',
        name varchar(10) not null unique comment '部门名称',
        create_time datetime  comment '创建时间',
        update_time datetime  comment '修改时间'
    ) comment '部门表';
    ​
    INSERT INTO `dept` VALUES (1,'学工部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
                            (2,'教研部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
                            (3,'咨询部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
                            (4,'就业部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
                            (5,'人事部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
                            (6,'行政部','2023-09-27 14:00:00','2023-09-27 14:00:00'),
                            (7,'综合部','2023-09-25 14:44:19','2023-09-25 14:44:19');
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.time.LocalDateTime;
    ​
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Dept {
        private Integer id;
        private String name;
        private LocalDateTime createTime;
        private LocalDateTime updateTime;
    }
    ​

  1. 在项目中引入Mybatis的起步依赖,mysql的驱动包

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
    ​
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

  1. 在项目的application.properties 中引入Mybatis的配置信息 (数据库连接、日志输出)

    #数据库连接信息
    spring.datasource.url=jdbc:mysql://localhost:3306/web01
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.username=root
    spring.datasource.password=root@1234
    ​
    #mybatis 日志
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

  1. 定义mapper包,并且定义DeptMapper接口,并声明接口方法。

    import com.itheima.pojo.Dept;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import java.util.List;
    ​
    @Mapper
    public interface DeptMapper {
    ​
        @Select("select * from dept")
        public List<Dept> findAll();
    ​
    }

  1. 改造之前编写的dao、service的代码,在service实现中注入mapper接口

    • dao层的代码不需要了(备份之后,可以删除)

    • service层的代码,需要注入Mapper接口,调用mapper接口方法查询数据库中的数据

      @Service
      public class DeptServiceImpl implements DeptService {
         
          @Autowired
          private DeptMapper deptMapper;
      ​
          @Override
          public List<Dept> queryDeptList() {
              return deptMapper.findAll();
          }
      }

  1. 启动服务,打开apifox进行测试

    		经过测试,我们发现,创建时间 createTime,修改时间 updateTime 属性并未成功封装。 接下来,我们就需要来处理数据封装问题。
    
    
    

3.2 数据封装

我们看到查询返回的结果中大部分字段是有值的,但是createTime,updateTime这两个字段是没有值的,而数据库中是有对应的字段值的,这是为什么呢?

原因如下:

  • 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。

  • 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

解决方案:

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

1. 起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样

@Select("select id, name, create_time createTime, update_time updateTime from dept")
public List<Dept> findAll();

2. 手动结果映射:通过 @Results及@Result 进行手动结果映射

@Results({@Result(column = "create_time", property = "createTime"),
          @Result(column = "update_time", property = "updateTime")})
@Select("select id, name, create_time, update_time from dept")
public List<Dept> findAll();

@Results源代码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
    String id() default "";
    Result[] value() default {};  //Result类型的数组
}

@Result源代码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Repeatable(Results.class)
public @interface Result {
    boolean id() default false;//表示当前列是否为主键(true:是主键)
    String column() default "";//指定表中字段名
    String property() default "";//指定类中属性名
}

3. 开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射

驼峰命名规则: abc_xyz => abcXyz

  • 表中字段名:abc_xyz

  • 类中属性名:abcXyz

# 在application.properties中添加:
mybatis.configuration.map-underscore-to-camel-case=true

要使用驼峰命名前提是 实体类的属性 与 数据库表中的字段名严格遵守驼峰命名。


学习如逆水行舟,不进则退,唯有不懈努力,才能超越自我。

  • 53
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值