OOP 面向对象 java 基础--服务+maven+mysql

开启一个简单的HTTP服务

测试类

java 复制代码
package apiStudy;

import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;

public class Test {
    public static void main() {
        InetSocketAddress socket = new InetSocketAddress(4399);
        try {
            HttpServer server = HttpServer.create(socket, 0);
            server.createContext("/api1", new api1());
            server.start();
            System.out.println("服务创建成功");
        } catch (IOException e) {
            throw new RuntimeException("服务创建失败" + e);
        }
    }
}

api1实现类

java 复制代码
package apiStudy;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import java.io.IOException;

public class api1 implements HttpHandler {

    @Override
    public void handle(HttpExchange exchange) {
        System.out.println("请求了API1" + exchange);
        try {
            String str = "hello word! 你好";
            exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=UTF-8");
            exchange.sendResponseHeaders(200, str.getBytes().length);
            exchange.getResponseBody().write(str.getBytes());
            exchange.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

maven 学习

前端中的 npm + vite = maven

package.json = pom.xml

不一定非要全局安装maven,后续使用框架创建项目的时候自带就行

java项目安装的依赖,会统一放在maven设置的安装目录下(为了避免重复安装),不会像前端那样,每个项目都有一个node_modules,都放在node_modules中,跟我们的pnpm很像

pom.xml

ini 复制代码
groupId + artifactId => 才可以找到对应的依赖
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>myMaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>25</maven.compiler.source>
        <maven.compiler.target>25</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- 👇 这一行,就能自动下载 Jackson,json序列化的包,不用你去官网下载! -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>

        <!-- 类似前端的loadsh -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>

    </dependencies>

</project>

使用servlet和tomcat开启服务

  • Tomcat = HTTP 服务器 + 运行环境

  • Servlet = 处理请求的代码规范

  • Tomcat 管网络,Servlet 管业务

  • Servlet 跑在 Tomcat 里面

你可以把 Tomcat 理解成:

一个接收 HTTP 请求、并转发给你代码的 "中间件 / 门卫 / 快递站"

它不负责业务逻辑,只负责网络通信

servlet

  • Servlet 是运行在服务器端的 Java 小程序

  • 作用:接收请求 → 处理 → 返回响应

  • 必须运行在 Tomcat 等容器中

  • 生命周期:init() → service() → destroy()

  • Java Web 所有框架的底层基础(Spring MVC/Spring Boot)

数据库

为什么不能用字符串存储日期类

  1. 字符串不会校验,可以存非法日期,不存在的日期,非日期
  2. 按时间段查询,排序时,日期更高效
  3. 数据库会自动维护日期类,且日期计算繁琐

DATETIME(常用) vs TIMESTAMP 对比

对比项 DATETIME TIMESTAMP
时间范围 1000-9999年 1970-2038年
存储空间 8 字节 4 字节
时区影响 不受影响 受时区影响
自动初始化 MySQL 5.6.5+ 支持 完全支持
自动更新 MySQL 5.6.5+ 支持 完全支持
索引性能 相同 相同
推荐场景 历史数据、未来时间 当前时间、跨时区应用

字段属性和约束

  1. NOT NULL

    • 确保字段的值不能为空(NULL)。
    • 示例email VARCHAR(100) NOT NULL(插入时 email 必须提供值)
  2. DEFAULT

    • 为字段设置默认值,当插入时未指定该字段时自动使用。
    • 示例status VARCHAR(20) DEFAULT 'active'
  3. AUTO_INCREMENT

    • 整数字段自动递增(通常用于主键),新记录的值 = 当前最大值 + 1。
    • 示例id INT AUTO_INCREMENT
  4. PRIMARY KEY

    • 主键,唯一标识一行记录,隐含 NOT NULL + UNIQUE。一个表只能有一个主键。
    • 示例user_id INT PRIMARY KEYPRIMARY KEY (id, role_id)(复合主键)
  5. UNIQUE

    • 确保字段(或字段组合)的值在整个表中不重复,但允许多个 NULL(NULL 彼此不冲突)。
    • 示例username VARCHAR(50) UNIQUE
  6. CHECK

    • 限制字段值必须满足指定的条件表达式(MySQL 8.0.16+ 才完全生效;低版本会解析但忽略)。
    • 示例age INT CHECK (age >= 18)
  7. FOREIGN KEY

    • 外键约束,确保字段值必须来自另一表的主键或唯一键,用于维护引用完整性。
    • 示例dept_id INT, FOREIGN KEY (dept_id) REFERENCES departments(id)
  8. COMMENT

    • 为字段添加说明文本,便于维护文档,不改变数据行为。
    • 示例price DECIMAL(10,2) COMMENT '商品单价,单位:元'
sql 复制代码
CREATE TABLE employees (
    emp_id      INT AUTO_INCREMENT PRIMARY KEY COMMENT '员工ID,自增主键',
    email       VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱,必填且唯一',
    name        VARCHAR(50)  NOT NULL COMMENT '姓名,必填',
    age         INT CHECK (age >= 18 AND age <= 65) COMMENT '年龄,18-65岁',
    status      VARCHAR(10)  DEFAULT 'active' COMMENT '状态,默认为active',
    dept_id     INT COMMENT '部门ID,关联departments表',
    created_at  TIMESTAMP    DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);

索引 INDEX

索引是数据库中用于快速查找数据的数据结构,类似于书的目录,如果有目录可以直接定位到想找到内容在哪一页,否则只能一页一页的找。没有索引,MySQL 必须从第一行开始逐行扫描全表(称为全表扫描),直到找到所需数据。

主键索引,在创建主键的时候,自动创建主键索引

唯一索引,给字段设置unique的时候,自动创建唯一索引

方法1:建表时直接创建索引(新项目直接创建)

sql 复制代码
CREATE TABLE users (
    -- 主键索引
    id INT PRIMARY KEY AUTO_INCREMENT,      
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    -- 唯一索引(自动创建)
    phone INT NOT NULL UNIQUE,              
    age INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    -- 普通索引
    INDEX idx_email (email)                 
);

方法2:表创建后单独添加索引(老项目优化索引)

sql 复制代码
-- 先创建表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    age INT,
    city VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 后添加普通索引
CREATE INDEX idx_email ON users(email);
作用 说明 类比
快速查询 大幅提升 SELECT 查询速度 书的目录直接翻到第100页
排序优化 避免 filesort,直接利用索引顺序 按拼音排序的通讯录
分组优化 加速 GROUP BY 操作 分类好的商品清单
唯一性保证 UNIQUE 索引确保数据不重复 身份证号不能重复
外键支持 外键约束需要索引支持 两表关联的桥梁
索引类型 关键词 允许重复 允许NULL 最大数量/表 底层结构 典型场景
主键索引 PRIMARY KEY 1个 B+Tree(聚簇) 唯一标识每行
唯一索引 UNIQUE ✅(多个NULL) 多个 B+Tree 邮箱、手机号
普通索引 INDEX 多个 B+Tree 常用查询列
复合索引 INDEX(a,b,c) 多个 B+Tree 多条件查询
全文索引 FULLTEXT 多个 倒排索引 文本搜索
空间索引 SPATIAL 多个 R-Tree 地理坐标

联表查询

1. INNER JOIN(内连接,最常用)

只返回两张表都匹配的数据。

sql 复制代码
SELECT orders.order_id, users.name, orders.amount
FROM orders
INNER JOIN users ON orders.user_id = users.user_id;

2. LEFT JOIN(左连接)

返回左表全部数据,右表没有匹配的显示为 NULL。

(以主表为主,查询出所有主表的内容,如果on没有匹配到对应的从表数据,从表数据全部以null的形式展示)

sql 复制代码
SELECT users.name, orders.amount
FROM users
LEFT JOIN orders ON users.user_id = orders.user_id;

3. RIGHT JOIN(右连接)

返回右表全部数据,左表没有匹配的显示为 NULL。

(以从表为主,查询出所有从表的内容,如果on没有匹配到对应的主表数据,主表数据全部以null的形式展示)

sql 复制代码
SELECT users.name, orders.amount
FROM users
RIGHT JOIN orders ON users.user_id = orders.user_id;
JOIN 类型 返回结果 使用频率
INNER JOIN 两表都匹配的记录 ⭐⭐⭐⭐⭐
LEFT JOIN 左表全部 + 右表匹配的 ⭐⭐⭐⭐
RIGHT JOIN 右表全部 + 左表匹配的

MySQL 聚合函数

函数 作用 返回类型
COUNT() 统计行数 整数
SUM() 计算总和 数值
AVG() 计算平均值 数值
MAX() 求最大值 与原类型相同
MIN() 求最小值 与原类型相同

1. COUNT() - 计数

sql 复制代码
-- 统计所有行(包括 NULL)
SELECT COUNT(*) FROM users;  -- 返回 100

-- 统计某列非 NULL 的行数
SELECT COUNT(email) FROM users;  -- 只统计有邮箱的

-- 去重统计
SELECT COUNT(DISTINCT city) FROM users;  -- 有多少个不同的城市

-- 带条件统计
SELECT COUNT(*) FROM users WHERE age >= 18;  -- 成年用户数

2. SUM() - 求和

sql 复制代码
-- 计算总金额
SELECT SUM(amount) FROM orders;  -- 所有订单金额总和

-- 带条件求和
SELECT SUM(amount) FROM orders WHERE status = 'paid';  -- 已支付订单总和

-- 条件求和(配合 IF)
SELECT SUM(IF(status = 'paid', amount, 0)) FROM orders;  -- 只累加已支付的

3. AVG() - 平均值

sql 复制代码
-- 计算平均年龄
SELECT AVG(age) FROM users;

-- 计算平均价格(保留2位小数)
SELECT ROUND(AVG(price), 2) FROM products;

-- 注意:AVG 自动忽略 NULL 值
SELECT AVG(score) FROM exam;  -- 缺考的不计入平均

4. MAX() / MIN() - 最大/最小值

sql 复制代码
-- 最高/最低价格
SELECT MAX(price), MIN(price) FROM products;

-- 最新/最早订单
SELECT MAX(create_time), MIN(create_time) FROM orders;

-- 结合条件
SELECT MAX(score) FROM exam WHERE subject = '数学';

配合 GROUP BY 使用(最重要)

sql 复制代码
-- 统计每个城市的用户数量
SELECT city, COUNT(*) AS user_count
FROM users
GROUP BY city;

-- 统计每个分类的商品数量和平均价格
SELECT 
    category,
    COUNT(*) AS product_count,
    AVG(price) AS avg_price,
    MAX(price) AS max_price
FROM products
GROUP BY category;

-- 统计每个用户的订单总金额
SELECT 
    user_id,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount
FROM orders
GROUP BY user_id;

配合 HAVING 使用(过滤分组)

sql 复制代码
-- 找出订单数大于5的用户
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING order_count > 5;

-- 找出平均价格高于500的商品分类
SELECT category, AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING avg_price > 500;

常见注意事项

注意点 说明 示例
NULL 处理 COUNT(列) 忽略 NULL,SUM/AVG 也忽略 缺考不计入平均分
COUNT(*) vs COUNT(列) * 统计所有行,(列) 统计非 NULL 区别在于 NULL
聚合函数不能用于 WHERE WHERE 在聚合前执行 WHERE SUM(amount) > 100
使用 HAVING 聚合后的条件用 HAVING HAVING COUNT(*) > 5

最常用的数值函数(7个)

函数 作用 示例 结果
ROUND() 四舍五入 ROUND(3.14159, 2) 3.14
CEIL() 向上取整 CEIL(3.14) 4
FLOOR() 向下取整 FLOOR(3.14) 3
ABS() 绝对值 ABS(-5) 5
MOD() 取余数 MOD(10, 3) 1
RAND() 随机数 RAND() 0.123456
ROUND() 取整 ROUND(3.5) 4

实战案例

1. 创建时间和更新时间

sql 复制代码
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    
    -- 创建时间:插入时自动设为当前时间,之后不再改变
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    -- 更新时间:插入时设为当前时间,每次更新时自动更新
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

分组,排序,分页

怎么理解group by

group by 就是根据后面的字段进行分组,整理

举一个简单的例子:SELECT dep_id,SUM(salary) FROM pyuser GROUP BY dep_id;

这句sql的意思就是,先根据dep_id进行分组,将相同dep_id的数据放在一个集合里面,再根据这个集合里的数据求和(求最大,最小,平均值)同一组数据中的 salary 字段,从后往前理解就一目了然
再举个例子,全世界所有人的信息都在一个表里,现在将表里的数据,根据国家分组,并求每个国家的人的平均工资

SELECT country,AVG(salary) FROM world_user GROUP BY country;

基本语法

sql 复制代码
SELECT 
    列1, 
    聚合函数(列2) 
FROM 表名
[WHERE 条件]
GROUP BY 列1
[HAVING 分组后条件]
[ORDER BY 列1 ASC/DESC];

注意事项

  • 将表中值相同的行归入同一组
  • 每个分组在结果中只输出一行
  • 出现在 SELECT 中的非聚合列必须出现在 GROUP BY 中(严格模式下会报错)
  • having不是必须跟在group by只是通常一起使用,没有group by的时候,having将整个表视为一个分组

havingwhere 的区别

对比维度 WHERE HAVING
执行时机 分组之前过滤行 分组之后过滤组
能否使用聚合函数 ❌ 不能(会报错) ✅ 能(如 SUM, COUNT, AVG 等)
能否使用列别名 ❌ 不能(MySQL 特定情况除外) ✅ 能
作用对象 原始表的行 分组后的结果集
能否单独使用 ✅ 可以 ✅ 可以(整个表视为一组)
与索引关系 可以命中索引 通常无法命中索引(操作结果集)

分页

基本语法

所谓的偏移量,就是跳过前面 x 条数据,从 x 条数据后才开始查询

sql 复制代码
SELECT 列1, 列2, ...
FROM 表名
[WHERE 条件]
[ORDER BY 排序字段]
LIMIT 每页行数 OFFSET 偏移量;

-- 简化写法(常用)
LIMIT 偏移量, 每页行数;
java 复制代码
int pageNum = 3;     // 当前页码
int pageSize = 10;   // 每页大小
int offset = (pageNum - 1) * pageSize;  // 偏移量 = 20
sql 复制代码
SELECT *
FROM 表名
[WHERE 条件]
LIMIT pageSize OFFSET offset;

-- 简化写法(常用)
LIMIT offset, pageSize;
相关推荐
fliter1 小时前
Rust 项目管理动态 — 2026 年 2 月
后端
苍何1 小时前
一个令人惊艳的开源项目,Agent Skill 开始自进化了?
后端
小研说技术2 小时前
Spring AI实现rag流程(简易版)
java·后端
Nturmoils2 小时前
自增主键别只会 auto_increment,先把值从哪来讲清楚
数据库·后端
Slice_cy2 小时前
基于node实现服务端内核引擎
前端·后端
神奇小汤圆2 小时前
什么是面向切面编程AOP?
后端
倾颜2 小时前
从手写 Runner 到 LangGraph:受控 Agent 接入 LangGraph
前端·后端·langchain
谁在黄金彼岸3 小时前
Lance模型解读
后端
神奇小汤圆3 小时前
深入理解MySQL事务隔离级别:MVCC机制与Next-Key Lock如何解决幻读问题?
后端