• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

youyou-dev

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Web后端基础(Mybatis)

Web后端基础(java操作数据库)

做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库的技术最为底层、最为基础的就是JDBC。

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

但是使用JDBC来操作数据库,会比较繁琐,所以现在在企业项目开发中呢,一般都会使用基于JDBC的封装的高级框架,比如:Mybatis、MybatisPlus、Hibernate、SpringDataJPA。

1、JDBC

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

有了JDBC之后,我们就可以直接在java代码中来操作数据库

1.1、查询数据

需求:基于JDBC实现用户登录功能。

本质:是基于JDBC程序,执行如下select语句,并将查询的结果输出到控制台。SQL语句:

select * from user where username = 'linchong' and password = '123456';

准备工作:

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

<dependencies>
    <!-- MySQL JDBC driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.3</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2). 在 src/main/test/java 目录下编写测试类,定义测试方法

public class JDBCTest {

    /**
     * 编写JDBC程序, 查询数据
     */
    @Test
    public void testJdbc() throws Exception {
        // 获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234");
        // 创建预编译的PreparedStatement对象
        PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?");
        // 设置参数
        pstmt.setString(1, "daqiao"); // 第一个问号对应的参数
        pstmt.setString(2, "123456"); // 第二个问号对应的参数
        // 执行查询
        ResultSet rs = pstmt.executeQuery();
        // 处理结果集
        while (rs.next()) {
            int id = rs.getInt("id");
            String uName = rs.getString("username");
            String pwd = rs.getString("password");
            String name = rs.getString("name");
            int age = rs.getInt("age");

            System.out.println("ID: " + id + ", Username: " + uName + ", Password: " + pwd + ", Name: " + name + ", Age: " + age);
        }
        // 关闭资源
        rs.close();
        pstmt.close();
        conn.close();
    }

}

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

public class JDBCTest {

    /**
     * 编写JDBC程序, 查询数据
     */
    @ParameterizedTest
    @CsvSource({"daqiao,123456"})
    public void testJdbc(String _username, String _password) throws Exception {
        // 获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234");
        // 创建预编译的PreparedStatement对象
        PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?");
        // 设置参数
        pstmt.setString(1, _username); // 第一个问号对应的参数
        pstmt.setString(2, _password); // 第二个问号对应的参数
        // 执行查询
        ResultSet rs = pstmt.executeQuery();
        // 处理结果集
        while (rs.next()) {
            int id = rs.getInt("id");
            String uName = rs.getString("username");
            String pwd = rs.getString("password");
            String name = rs.getString("name");
            int age = rs.getInt("age");

            System.out.println("ID: " + id + ", Username: " + uName + ", Password: " + pwd + ", Name: " + name + ", Age: " + age);
        }
        // 关闭资源
        rs.close();
        pstmt.close();
        conn.close();
    }

}

1.2、代码剖析

ResultSet

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

  • next():将光标从当前位置向前移动一行,并判断当前行是否为有效行,返回值为boolean。
    • true:有效行,当前行有数据
    • false:无效行,当前行没有数据
  • getXxx(…):获取数据,可以根据列的编号获取,也可以根据列名获取(推荐)。

预编译SQL

  • 静态SQL(参数硬编码)
conn.prepareStatement("SELECT * FROM user WHERE username = 'daqiao' AND password = '123456'");
ResultSet resultSet = pstmt.executeQuery();

这种是参数值,直接拼接在SQL语句中,参数值是写死的。

  • 预编译SQL(参数动态传递)
conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?");
pstmt.setString(1, "daqiao");
pstmt.setString(2, "123456");
ResultSet resultSet = pstmt.executeQuery();

这种并未将参数值在SQL语句中写死,而是使用 ? 进行占位,然后再指定每一个占位符对应的值是多少,而最终在执行SQL语句的时候,程序会将SQL语句(SELECT * FROM user WHERE username = ? AND password = ?),以及参数值("daqiao", "123456")都发送给数据库,然后在执行的时候,会使用参数值,将?占位符替换掉。

这种预编译的SQL,是在项目开发中推荐使用的SQL语句。主要的作用有两个:防止SQL注入;性能更高

SQL注入

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

SQL注入最典型的场景,就是用户登录功能。当应用程序将用户输入直接拼接到SQL查询语句中,且未做任何过滤或参数化处理时,攻击者就可以利用特殊字符(如单引号 '、分号 ;、注释符 -- 等)改变原SQL语句的结构。

SQL注入解决

通过预编译SQL(select * from user where username = ? and password = ?),就可以直接解决上述SQL注入的问题。

2、MyBatis

什么是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.1、JDBC VS Mybatis

JDBC程序的缺点:

  • url、username、password 等相关参数全部硬编码在java代码中。
  • 查询结果的解析、封装比较繁琐。
  • 每一次操作数据库之前,先获取连接,操作完毕之后,关闭连接。 频繁的获取连接、释放连接造成资源浪费。

mybatis解决这些问题的方法:

  • 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.properties中
  • 查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注
  • 在mybatis中使用了数据库连接池技术,从而避免了频繁的创建连接、销毁连接而带来的资源浪费。

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

2.2、数据库连接池

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

1). 没有数据库连接池的情况

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

2). 有数据库连接池的情况

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

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

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

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

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

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

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

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

数据库连接池的好处:

  • 资源重用
  • 提升系统响应速度
  • 避免数据库连接遗漏

3)实现数据库连接池

  • 官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口)
  • 功能:获取连接
    • public Connection getConnection() throws SQLException;
  • 第三方组织必须按照DataSource接口实现

常见的数据库连接池:C3P0 、DBCP 、Druid 、Hikari (springboot默认)

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

2.3、增删改查操作

删除

  • 需求:根据ID删除用户信息

  • SQL:delete from user where id = 5;

  • Mapper接口方法:

    • 方式一:
    /**
     * 根据id删除
     */
    @Delete("delete from user where id = 5")
    public void deleteById();
    //这种方式执行删除操作,调用deleteById方法只能删除id为5的用户信息,因为将id直接写死在代码中了,不可取。
    
    • 方式二:
    /**
     * 根据id删除
     */
    @Delete("delete from user where id = #{id}")
    public void deleteById(Integer id);
    

​ 在Mybatis中,我们可以通过参数占位符号 #{...} 来占位,在调用deleteById方法时,传递的参数值,最终会替换占位符。

  • 编写单元测试方法进行测试

    • 在单元测试类中,增加如下测试方法.
    @Test
    public void testDeleteById(){
        userMapper.deleteById(36);
    }
    
  • DML语句执行完毕,是有返回值的,我们可以为Mapper接口方法定义返回值来接收,如下:

    /**
     * 根据id删除
     */
    @Delete("delete from user where id = #{id}")
    public Integer deleteById(Integer id);
    

Integer类型的返回值,表示DML语句执行完毕影响的记录数。

  • Mybatis的提供的符号,有两个,一个是 #{...},另一个是 ${...},区别如下:
符号 说明 场景 优缺点
# 占位符。执行时,会将#{…}替换为?,生成预编译SQL 参数值传递 安全、性能高 (推荐)
$ 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 表名、字段名动态设置时使用 不安全、性能低

那在企业项目开发中,强烈建议使用 #{...} 。

新增

  • 需求:添加一个用户
  • SQL:insert into user(username,password,name,age) values('zhouyu','123456','周瑜',20);
  • Mapper接口:
/**
 * 添加用户
 */
@Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})")
public void insert(User user);

如果在SQL语句中,我们需要传递多个参数,我们可以把多个参数封装到一个对象中。然后在SQL语句中,我们可以通过#{对象``属性``名}的方式,获取到对象中封装的属性值。

  • 单元测试:

在测试类中添加测试方法,代码如下:

@Test
public void testInsert(){
    User user = new User();
    user.setUsername("admin");
    user.setPassword("123456");
    user.setName("管理员");
    user.setAge(30);
    userMapper.insert(user);
}

修改

  • 需求:根据ID更新用户信息
  • SQL:update user set username = 'zhouyu', password = '123456', name = '周瑜', age = 20 where id = 1;
  • Mapper接口方法:
/**
 * 根据id更新用户信息
 */
@Update("update user set username = #{username},password = #{password},name = #{name},age = #{age} where id = #{id}")
public void update(User user);
  • 单元测试:

在测试类中添加测试方法,代码如下:

@Test
public void testUpdate(){
    User user = new User();
    user.setId(6);
    user.setUsername("admin666");
    user.setPassword("123456");
    user.setName("管理员");
    user.setAge(30);
    userMapper.update(user);
}

查询

  • 需求:根据用户名和密码查询用户信息
  • SQL:select* fromuser whereusername = 'zhouyu' and password = '123456'
  • Mapper接口方法:
/**
 * 根据用户名和密码查询用户信息
 */
@Select("select * from user where username = #{username} and password = #{password}")
public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);

@param注解的作用是为接口的方法形参起名字的。(由于用户名唯一的,所以查询返回的结果最多只有一个,可以直接封装到一个对象中)

  • 单元测试:

在测试类中添加测试方法,代码如下:

@Test
public void testFindByUsernameAndPassword(){
    User user = userMapper.findByUsernameAndPassword("admin666", "123456");
    System.out.println(user);
}

2.4、XML映射配置

Mybatis的开发有两种方式:注解、XML

ML配置文件规范

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

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

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
  2. XML映射文件的namespace属性为Mapper接口全限定名一致
  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

<select>标签:就是用于编写select查询语句的。resultType属性,指的是查询返回的单条记录所封装的类型。

XML配置文件实现

第1步: 创建XML映射文件

img

image-20260411120938386

第2步:编写XML映射文件

xml映射文件中的dtd约束,直接从mybatis官网复制即可; 或者直接AI生成。

<?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.uu.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.uu.mapper.EmpMapper">

    <!--查询操作-->
    <select id="findAll" resultType="com.uu.pojo.User">
        select * from user
    </select>
    
</mapper>

resultType 属性的值,与查询返回的单条记录封装的类型一致。

注意:一个接口方法对应的SQL语句,要么使用注解配置,要么使用XML配置,切不可同时配置。

3、SpringBoot配置文件

3.1、介绍

前面我们一直使用springboot项目创建完毕后自带的application.properties进行属性的配置,而如果在项目中,我们需要配置大量的属性,采用properties配置文件这种 key=value 的配置形式,就会显得配置文件的层级结构不清晰,也比较臃肿。

其实在springboot项目当中是支持多种配置方式的,除了支持properties配置文件以外,还支持另外一种类型的配置文件,也就是yml格式的配置文件。yml格式配置文件名字为:application.yaml , application.yml 这两个配置文件的后缀名虽然不一样,但是里面配置的内容形式都是一模一样的。

对比一下,采用 application.properties 和 application.yml 来配置同一段信息(数据库连接信息),两者之间的配置对比:

img

img

在项目开发中,推荐使用application.yml配置文件来配置信息,简洁、明了、以数据为中心。

3.2、语法

简单的了解过springboot所支持的配置文件,以及不同类型配置文件之间的优缺点之后,接下来是yml配置文件的基本语法:

  • 大小写敏感
  • 数值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略

img

了解完yml格式配置文件的基本语法之后,接下来我们再来看下yml文件中常见的数据格式。在这里我们主要介绍最为常见的两类:

  1. 定义对象或Map集合
  2. 定义数组、list或set集合
  • 对象/Map集合
user:
  name: zhangsan
  age: 18
  password: 123456
  • 数组/List/Set集合
hobby: 
  - java
  - game
  - sport

在yml格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 '' 引起来,因为以0开头在yml中表示8进制的数据。

3.3、案例

熟悉完了yml文件的基本语法后,我们修改下之前案例中使用的配置文件,变更为application.yml配置方式:

  1. 修改application.properties名字为:_application.properties(名字随便更换,只要加载不到即可)
  2. 创建新的配置文件: application.yml
  • 原有的 application.properties 配置文件

img

  • 新建的 application.yml 配置文件

img

配置文件的内容如下:

#数据源配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/web01
    username: root
    password: root@1234
#mybatis配置
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

以上笔记内容来自黑马程序员

posted on 2026-04-11 12:21  U~U  阅读(6)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3