前言: 在软件开发的众多场景中,与数据库进行交互是一项核心任务。

目录

一、JDBC 是啥玩意儿?

二、查询数据,把数据库当字典查

三、更新数据,给数据库动个小手术

四、PreparedStatement,给 SQL 戴个口罩防病毒

五、连接池,数据库连接的“共享单车”

六、事务,要么一起成,要么一起败

七、数据库元数据,瞧瞧数据库的底细

八、实战演练

搭建一个简易博客系统

需求分析

数据库设计

用户注册与登录模块

给博客文章添加标签功能

修改数据库表结构

添加标签功能代码

使用连接池优化博客系统

添加 Maven 依赖

使用 HikariCP 连接池重构代码

实现博客文章的点赞功能

修改数据库表结构

添加点赞功能代码

总结


一、JDBC 是啥玩意儿?

JDBC 就像是个翻译官,它能让 Java 和数据库这个外国友人对上话。靠着它, Java 就能跟数据库说:“嘿,把那啥数据给我看看!”或者“把这数据给我存好喽!”

咱先整点儿实在的,把环境搭起来。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnector {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db"; // 数据库地址,就像去朋友家的路
        String username = "root"; // 数据库的门牌号
        String password = "123456"; // 密码,就跟咱家wifi密码似的

        try {
            Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
            System.out.println("哇塞!数据库连上了,这感觉就像是打通了任督二脉!");
            // 后面就能撒欢儿地玩数据库啦
        } catch (SQLException e) {
            System.err.println("哎呀妈呀!连数据库都失败了,这路咋走不通呢?" + e.getMessage());
        }
    }
}

二、查询数据,把数据库当字典查

连上数据库了,咱就得干点正经事儿,先从查数据开始。

import java.sql.ResultSet;
import java.sql.Statement;

public class DataQuerier {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery("SELECT id, name, age FROM people")) {

            System.out.println("查询结果来啦,瞧一瞧,看一看!");
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                System.out.printf(" ID:%d ,名字:%s ,年龄:%d ,这人看起来挺有意思!%n", id, name, age);
            }
        } catch (SQLException e) {
            System.err.println("查询失败,莫非是 SQL 写错了?" + e.getMessage());
        }
    }
}

三、更新数据,给数据库动个小手术

光会查可不行,还得会更新数据,这就像是给数据库动个小手术,把旧数据换个新模样。

public class DataUpdater {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.Statement statement = connection.createStatement()) {

            int rowsAffected = statement.executeUpdate("UPDATE people SET age = 30 WHERE name = '张三'");
            System.out.println("更新成功!一共改了" + rowsAffected + "行,这人张三现在年龄变年轻啦!");
        } catch (SQLException e) {
            System.err.println("更新失败,难道是定位不到张三?" + e.getMessage());
        }
    }
}

四、PreparedStatement,给 SQL 戴个口罩防病毒

直接用 Statement 像是光着膀子在干活,容易被 SQL 注入这种“病毒”盯上。PreparedStatement 就像是给 SQL 戴了个口罩,安全多了。

public class PreparedStatementDemo {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {

            // 插入数据,像往数据库的篮子里放个新苹果
            String insertSQL = "INSERT INTO people (name, age) VALUES (?, ?)";
            try (java.sql.PreparedStatement pstmtInsert = connection.prepareStatement(insertSQL)) {
                pstmtInsert.setString(1, "李四");
                pstmtInsert.setInt(2, 25);
                int rowsAffected = pstmtInsert.executeUpdate();
                System.out.println("插入成功!新来的李四已经入住数据库小区啦,影响了" + rowsAffected + "行。");
            }

            // 查询数据,就像在人群里找一个戴眼镜的
            String querySQL = "SELECT id, name, age FROM people WHERE name = ?";
            try (java.sql.PreparedStatement pstmtQuery = connection.prepareStatement(querySQL)) {
                pstmtQuery.setString(1, "李四");
                try (java.sql.ResultSet resultSet = pstmtQuery.executeQuery()) {
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        int age = resultSet.getInt("age");
                        System.out.printf("找到了!ID:%d ,名字:%s ,年龄:%d ,这人就是我们要找的李四!%n", id, name, age);
                    }
                }
            }
        } catch (SQLException e) {
            System.err.println("操作失败,是 SQL 写错还是别的啥问题?" + e.getMessage());
        }
    }
}

五、连接池,数据库连接的“共享单车”

每次都去创建、关闭数据库连接,就像每次都去买一辆新车,太费事儿。连接池就像是共享单车,用完放回去,别人还能接着用。

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class ConnectionPoolDemo {
    public static void main(String[] args) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/my_db");
        config.setUsername("root");
        config.setPassword("123456");
        config.setMaximumPoolSize(10); // 最多能借出 10 辆“单车”
        config.setMinimumIdle(2); // 至少得留 2 辆“单车”在池子里备用
        config.setIdleTimeout(30000); // 如果一辆“单车”闲了 30 秒,就回收了

        HikariDataSource dataSource = new HikariDataSource(config);

        try (Connection connection = dataSource.getConnection()) {
            System.out.println("从连接池成功借到连接,就像从共享单车池里抽出一辆车!");
            // 用这连接干点啥事儿...
        } catch (SQLException e) {
            System.err.println("从连接池借连接失败,这池子是不是干了?" + e.getMessage());
        }
    }
}

六、事务,要么一起成,要么一起败

有些事儿不能半途而废,比如转账,要么全转成功,要么啥都不干。这事儿就靠事务来撑腰。

public class TransactionDemo {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
            connection.setAutoCommit(false); // 先跟数据库说一声,咱要开始个重要操作啦

            try {
                // 假装这是从一个账户扣钱
                String sql1 = "UPDATE account SET balance = balance - 100 WHERE account_id = 1";
                java.sql.PreparedStatement pstmt1 = connection.prepareStatement(sql1);
                pstmt1.executeUpdate();

                // 假装这是给另一个账户加钱
                String sql2 = "UPDATE account SET balance = balance + 100 WHERE account_id = 2";
                java.sql.PreparedStatement pstmt2 = connection.prepareStatement(sql2);
                pstmt2.executeUpdate();

                connection.commit(); // 俩操作都成功啦,commit 提交,跟说“咱这事儿成啦”
                System.out.println("转账成功!钱都转好了,大家都开心!");
            } catch (SQLException e) {
                connection.rollback(); // 要是中途出岔子,rollback 回滚,就像啥事儿都没发生过
                System.err.println("转账失败,把钱都退回去,这事儿黄了!" + e.getMessage());
            }
        } catch (SQLException e) {
            System.err.println("数据库操作有毛病,快检查检查!" + e.getMessage());
        }
    }
}

七、数据库元数据,瞧瞧数据库的底细

数据库也有不少小秘密,元数据就像是它的户口本,能瞅瞅它都有啥家当。

import java.sql.DatabaseMetaData;

public class MetadataDemo {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/my_db";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
            DatabaseMetaData metaData = connection.getMetaData();

            // 瞅瞅数据库都有啥表,就像逛超市看都有啥货架
            try (java.sql.ResultSet tables = metaData.getTables(null, null, null, new String[]{"TABLE"})) {
                System.out.println("咱来看看数据库都有啥表!");
                while (tables.next()) {
                    String tableName = tables.getString("TABLE_NAME");
                    System.out.println("瞅见一张表,名字叫:" + tableName);
                }
            }

            // 瞅瞅某张表都有啥列,就像看菜谱都写了啥材料
            try (java.sql.ResultSet columns = metaData.getColumns(null, null, "people", null)) {
                System.out.println("咱来看看 people 表都有啥列!");
                while (columns.next()) {
                    String columnName = columns.getString("COLUMN_NAME");
                    String columnType = columns.getString("TYPE_NAME");
                    int columnSize = columns.getInt("COLUMN_SIZE");
                    System.out.printf("瞅见一列,名字:%s,类型:%s,长度:%d%n", columnName, columnType, columnSize);
                }
            }
        } catch (SQLException e) {
            System.err.println("瞅数据库底细都瞅不成,这都啥情况?" + e.getMessage());
        }
    }
}

八、实战演练

搭建一个简易博客系统

需求分析

为了让大家更好地理解 Java 数据库操作,咱们来搭建一个简易的博客系统。这个系统需要支持以下功能:

  1. 用户注册与登录

  2. 博客文章的增删改查

  3. 用户评论功能

数据库设计

先设计好数据库,这就像盖房子先搭好骨架。

CREATE DATABASE blog_system;

USE blog_system;

-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 博客文章表
CREATE TABLE blog_posts (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- 评论表
CREATE TABLE comments (
    id INT PRIMARY KEY AUTO_INCREMENT,
    post_id INT NOT NULL,
    user_id INT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (post_id) REFERENCES blog_posts(id),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

用户注册与登录模块

import java.sql.*;
import java.util.Scanner;

public class BlogUserManager {
    private static final String jdbcUrl = "jdbc:mysql://localhost:3306/blog_system";
    private static final String username = "root";
    private static final String password = "123456";

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("欢迎来到博客系统!");
        System.out.println("1. 注册");
        System.out.println("2. 登录");
        System.out.print("请选择:");

        int choice = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符

        if (choice == 1) {
            registerUser(scanner);
        } else if (choice == 2) {
            login(scanner);
        } else {
            System.out.println("别乱选啊,瞎捣鼓!");
        }

        scanner.close();
    }

    // 用户注册
    private static void registerUser(Scanner scanner) {
        System.out.print("请输入用户名:");
        String inputUsername = scanner.nextLine();
        System.out.print("请输入密码:");
        String inputPassword = scanner.nextLine();
        System.out.print("请输入邮箱:");
        StringinputEmail = scanner.nextLine();

        String insertSQL = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.PreparedStatement pstmt = connection.prepareStatement(insertSQL)) {

            pstmt.setString(1, inputUsername);
            pstmt.setString(2, inputPassword); // 实际项目中密码应该加密存储
            pstmt.setString(3, inputEmail);

            int rowsAffected = pstmt.executeUpdate();

            if (rowsAffected > 0) {
                System.out.println("注册成功!快去登录瞅瞅吧!");
            } else {
                System.out.println("注册失败,可能是用户名或邮箱重复了!");
            }

        } catch (SQLException e) {
            if (e.getErrorCode() == 1062) { // 判断是不是重复键值错误
                System.out.println("哎呀,这用户名或邮箱已经被别人用啦,换个试试!");
            } else {
                System.out.println("注册出问题啦:" + e.getMessage());
            }
        }
    }

    // 用户登录
    private static void login(Scanner scanner) {
        System.out.print("请输入用户名:");
        String inputUsername = scanner.nextLine();
        System.out.print("请输入密码:");
        String inputPassword = scanner.nextLine();

        String querySQL = "SELECT id, username FROM users WHERE username = ? AND password = ?";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.PreparedStatement pstmt = connection.prepareStatement(querySQL)) {

            pstmt.setString(1, inputUsername);
            pstmt.setString(2, inputPassword);

            try (java.sql.ResultSet resultSet = pstmt.executeQuery()) {

                if (resultSet.next()) {
                    int userId = resultSet.getInt("id");
                    String usernameResult = resultSet.getString("username");
                    System.out.println("登录成功!欢迎," + usernameResult + "!");
                    // 登录成功后可以进入博客系统主界面
                    showBlogSystemMenu(scanner, userId);
                } else {
                    System.out.println("用户名或密码不对,登录失败!");
                }
            }

        } catch (SQLException e) {
            System.out.println("登录出问题啦:" + e.getMessage());
        }
    }

    // 博客系统主菜单
    private static void showBlogSystemMenu(Scanner scanner, int userId) {
        while (true) {
            System.out.println("\n博客系统主菜单:");
            System.out.println("1. 发表新文章");
            System.out.println("2. 查看所有文章");
            System.out.println("3. 评论文章");
            System.out.println("4. 退出登录");
            System.out.print("请选择:");

            int choice = scanner.nextInt();
            scanner.nextLine(); // 消耗换行符

            if (choice == 1) {
                publishNewPost(scanner, userId);
            } else if (choice == 2) {
                viewAllPosts(scanner);
            } else if (choice == 3) {
                commentOnPost(scanner, userId);
            } else if (choice == 4) {
                System.out.println("退出登录,欢迎下次再来!");
                break;
            } else {
                System.out.println("别乱选,没这选项!");
            }
        }
    }

    // 发表新文章
    private static void publishNewPost(Scanner scanner, int userId) {
        System.out.print("请输入文章标题:");
        String title = scanner.nextLine();
        System.out.print("请输入文章内容:");
        String content = scanner.nextLine();

        String insertSQL = "INSERT INTO blog_posts (user_id, title, content) VALUES (?, ?, ?)";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.PreparedStatement pstmt = connection.prepareStatement(insertSQL)) {

            pstmt.setInt(1, userId);
            pstmt.setString(2, title);
            pstmt.setString(3, content);

            int rowsAffected = pstmt.executeUpdate();

            if (rowsAffected > 0) {
                System.out.println("文章发表成功!你可真是个写作小能手!");
            } else {
                System.out.println("文章发表失败,这是咋回事儿?");
            }

        } catch (SQLException e) {
            System.out.println("发表文章出问题啦:" + e.getMessage());
        }
    }

    // 查看所有文章
    private static void viewAllPosts(Scanner scanner) {
        String querySQL = "SELECT bp.id, bp.title, bp.content, bp.created_at, u.username " +
                          "FROM blog_posts bp " +
                          "JOIN users u ON bp.user_id = u.id " +
                          "ORDER BY bp.created_at DESC";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.PreparedStatement pstmt = connection.prepareStatement(querySQL);
             java.sql.ResultSet resultSet = pstmt.executeQuery()) {

            System.out.println("\n所有文章:");
            while (resultSet.next()) {
                int postId = resultSet.getInt("bp.id");
                String title = resultSet.getString("bp.title");
                String content = resultSet.getString("bp.content");
                String createdAt = resultSet.getTimestamp("bp.created_at").toString();
                String author = resultSet.getString("u.username");

                System.out.printf("文章ID:%d,标题:%s,作者:%s,发布时间:%s%n", 
                                 postId, title, author, createdAt);
                System.out.println("内容:" + content);
                System.out.println("-----------------------------------");
            }

        } catch (SQLException e) {
            System.out.println("查看文章出问题啦:" + e.getMessage());
        }
    }

    // 评论文章
    private static void commentOnPost(Scanner scanner, int userId) {
        System.out.print("请输入要评论的文章ID:");
        int postId = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符
        System.out.print("请输入评论内容:");
        String commentContent = scanner.nextLine();

        String insertSQL = "INSERT INTO comments (post_id, user_id, content) VALUES (?, ?, ?)";

        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
             java.sql.PreparedStatement pstmt = connection.prepareStatement(insertSQL)) {

            pstmt.setInt(1, postId);
            pstmt.setInt(2, userId);
            pstmt.setString(3, commentContent);

            int rowsAffected = pstmt.executeUpdate();

            if (rowsAffected > 0) {
                System.out.println("评论成功!你的观点很重要哦!");
            } else {
                System.out.println("评论失败,可能是文章ID不存在!");
            }

        } catch (SQLException e) {
            System.out.println("评论出问题啦:" + e.getMessage());
        }
    }
}

给博客文章添加标签功能

为了提升博客系统的功能,咱们再给文章加个标签功能。这样用户就能按标签搜索文章啦。

修改数据库表结构
-- 标签表
CREATE TABLE tags (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL UNIQUE
);

-- 文章标签关联表
CREATE TABLE post_tags (
    post_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (post_id, tag_id),
    FOREIGN KEY (post_id) REFERENCES blog_posts(id),
    FOREIGN KEY (tag_id) REFERENCES tags(id)
);
添加标签功能代码
// 在 BlogUserManager 类中添加以下方法

// 给文章添加标签
private static void addTagsToPost(Scanner scanner, int userId) {
    System.out.print("请输入要添加标签的文章ID:");
    int postId = scanner.nextInt();
    scanner.nextLine(); // 消耗换行符

    System.out.print("请输入标签名称(多个标签用逗号分隔):");
    String tagNames = scanner.nextLine();

    // 先插入标签,再关联文章和标签
    String insertTagSQL = "INSERT INTO tags (name) VALUES (?) ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)";
    String insertPostTagSQL = "INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)";

    try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
        connection.setAutoCommit(false); // 开始事务

        try (
            java.sql.PreparedStatement pstmtInsertTag = connection.prepareStatement(insertTagSQL);
            java.sql.PreparedStatement pstmtInsertPostTag = connection.prepareStatement(insertPostTagSQL)
        ) {
            String[] tags = tagNames.split(",");

            for (String tagName : tags) {
                tagName = tagName.trim();

                // 插入标签,如果标签已存在,则获取其ID
                pstmtInsertTag.setString(1, tagName);
                int tagInsertResult = pstmtInsertTag.executeUpdate();

                int tagId = 0;
                try (java.sql.ResultSet generatedKeys = pstmtInsertTag.getGeneratedKeys()) {
                    if (generatedKeys.next()) {
                        tagId = generatedKeys.getInt(1);
                    } else {
                        // 如果标签已存在,通过查询获取ID
                        String queryTagIdSQL = "SELECT id FROM tags WHERE name = ?";
                        try (java.sql.PreparedStatement pstmtQueryTagId = connection.prepareStatement(queryTagIdSQL)) {
                            pstmtQueryTagId.setString(1, tagName);
                            try (java.sql.ResultSet resultSet = pstmtQueryTagId.executeQuery()) {
                                if (resultSet.next()) {
                                    tagId = resultSet.getInt("id");
                                }
                            }
                        }
                    }
                }

                if (tagId > 0) {
                    // 关联文章和标签
                    pstmtInsertPostTag.setInt(1, postId);
                    pstmtInsertPostTag.setInt(2, tagId);
                    pstmtInsertPostTag.executeUpdate();
                }
            }

            connection.commit(); // 提交事务
            System.out.println("标签添加成功!文章现在有新标签啦!");
        } catch (SQLException e) {
            connection.rollback(); // 回滚事务
            System.out.println("添加标签失败啦:" + e.getMessage());
        }
    } catch (SQLException e) {
        System.out.println("数据库操作出问题啦:" + e.getMessage());
    }
}

// 按标签搜索文章
private static void searchPostsByTag(Scanner scanner) {
    System.out.print("请输入要搜索的标签:");
    String tag = scanner.nextLine();

    String querySQL = "SELECT bp.id, bp.title, bp.content, bp.created_at, u.username " +
                      "FROM blog_posts bp " +
                      "JOIN post_tags pt ON bp.id = pt.post_id " +
                      "JOIN tags t ON pt.tag_id = t.id " +
                      "JOIN users u ON bp.user_id = u.id " +
                      "WHERE t.name = ? " +
                      "ORDER BY bp.created_at DESC";

    try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
         java.sql.PreparedStatement pstmt = connection.prepareStatement(querySQL);
         java.sql.ResultSet resultSet = pstmt.executeQuery()) {

        System.out.println("\n搜索结果:");
        boolean found = false;
        while (resultSet.next()) {
            found = true;
            int postId = resultSet.getInt("bp.id");
            String title = resultSet.getString("bp.title");
            String content = resultSet.getString("bp.content");
            String createdAt = resultSet.getTimestamp("bp.created_at").toString();
            String author = resultSet.getString("u.username");

            System.out.printf("文章ID:%d,标题:%s,作者:%s,发布时间:%s%n", 
                             postId, title, author, createdAt);
            System.out.println("内容:" + content);
            System.out.println("-----------------------------------");
        }

        if (!found) {
            System.out.println("没找到带这个标签的文章,换个标签试试?");
        }

    } catch (SQLException e) {
        System.out.println("搜索文章出问题啦:" + e.getMessage());
    }
}

// 修改主菜单,添加新选项
private static void showBlogSystemMenu(Scanner scanner, int userId) {
    while (true) {
        System.out.println("\n博客系统主菜单:");
        System.out.println("1. 发表新文章");
        System.out.println("2. 查看所有文章");
        System.out.println("3. 评论文章");
        System.out.println("4. 给文章添加标签");
        System.out.println("5. 按标签搜索文章");
        System.out.println("6. 退出登录");
        System.out.print("请选择:");

        int choice = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符

        if (choice == 1) {
            publishNewPost(scanner, userId);
        } else if (choice == 2) {
            viewAllPosts(scanner);
        } else if (choice == 3) {
            commentOnPost(scanner, userId);
        } else if (choice == 4) {
            addTagsToPost(scanner, userId);
        } else if (choice == 5) {
            searchPostsByTag(scanner);
        } else if (choice == 6) {
            System.out.println("退出登录,欢迎下次再来!");
            break;
        } else {
            System.out.println("别乱选,没这选项!");
        }
    }
}

使用连接池优化博客系统

随着用户增多,直接获取数据库连接的方式可能会拖慢系统。咱们用连接池来优化一下。

添加 Maven 依赖
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>
使用 HikariCP 连接池重构代码
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

// 在 BlogUserManager 类中添加以下静态变量和方法
private static HikariDataSource dataSource;

// 初始化连接池
private static void initializeDataSource() {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/blog_system");
    config.setUsername("root");
    config.setPassword("123456");
    config.setMaximumPoolSize(10); // 最大连接数
    config.setMinimumIdle(2); // 最小空闲连接数
    config.setIdleTimeout(30000); // 空闲超时时间(毫秒)
    config.setConnectionTimeout(5000); // 连接超时时间(毫秒)

    dataSource = new HikariDataSource(config);
    System.out.println("数据库连接池初始化成功!就像准备了一池子的水,随时可以取用!");
}

// 从连接池获取连接
private static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
}

// 修改主方法,初始化连接池
public static void main(String[] args) {
    initializeDataSource(); // 初始化连接池
    Scanner scanner = new Scanner(System.in);

    System.out.println("欢迎来到博客系统!");
    System.out.println("1. 注册");
    System.out.println("2. 登录");
    System.out.print("请选择:");

    int choice = scanner.nextInt();
    scanner.nextLine(); // 消耗换行符

    if (choice == 1) {
        registerUser(scanner);
    } else if (choice == 2) {
        login(scanner);
    } else {
        System.out.println("别乱选啊,瞎捣鼓!");
    }

    scanner.close();
    dataSource.close(); // 关闭连接池
}

// 修改所有获取连接的地方,使用连接池获取连接
// 例如,将下面代码:
// try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 修改为:
// try (Connection connection = getConnection();

实现博客文章的点赞功能

再给博客加个点赞功能,这样用户就能表达对文章的喜爱啦。

修改数据库表结构
-- 点赞表
CREATE TABLE likes (
    post_id INT NOT NULL,
    user_id INT NOT NULL,
    liked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (post_id, user_id),
    FOREIGN KEY (post_id) REFERENCES blog_posts(id),
    FOREIGN KEY (user_id) REFERENCES users(id)
);
添加点赞功能代码
// 在 BlogUserManager 类中添加以下方法

// 点赞文章
private static void likePost(Scanner scanner, int userId) {
    System.out.print("请输入要点赞的文章ID:");
    int postId = scanner.nextInt();
    scanner.nextLine(); // 消耗换行符

    String insertSQL = "INSERT INTO likes (post_id, user_id) VALUES (?, ?)";
    String querySQL = "SELECT COUNT(*) AS like_count FROM likes WHERE post_id = ?";

    try (Connection connection = getConnection();
         java.sql.PreparedStatement pstmtInsert = connection.prepareStatement(insertSQL);
         java.sql.PreparedStatement pstmtQuery = connection.prepareStatement(querySQL)) {

        // 点赞
        pstmtInsert.setInt(1, postId);
        pstmtInsert.setInt(2, userId);
        int rowsAffected = pstmtInsert.executeUpdate();

        if (rowsAffected > 0) {
            // 查询当前点赞数
            pstmtQuery.setInt(1, postId);
            try (java.sql.ResultSet resultSet = pstmtQuery.executeQuery()) {
                if (resultSet.next()) {
                    int likeCount = resultSet.getInt("like_count");
                    System.out.println("点赞成功!这篇文章现在有 " + likeCount + " 个赞啦!");
                }
            }
        } else {
            System.out.println("你已经点过赞啦,别重复点赞哦!");
        }

    } catch (SQLException e) {
        System.out.println("点赞出问题啦:" + e.getMessage());
    }
}

// 查看带点赞数的文章列表
private static void viewPosts_withLikes(Scanner scanner) {
    String querySQL = "SELECT bp.id, bp.title, bp.content, bp.created_at, u.username, " +
                      "COUNT(l.user_id) AS like_count " +
                      "FROM blog_posts bp " +
                      "JOIN users u ON bp.user_id = u.id " +
                      "LEFT JOIN likes l ON bp.id = l.post_id " +
                      "GROUP BY bp.id, bp.title, bp.content, bp.created_at, u.username " +
                      "ORDER BY bp.created_at DESC";

    try (Connection connection = getConnection();
         java.sql.PreparedStatement pstmt = connection.prepareStatement(querySQL);
         java.sql.ResultSet resultSet = pstmt.executeQuery()) {

        System.out.println("\n所有文章(带点赞数):");
        while (resultSet.next()) {
            int postId = resultSet.getInt("bp.id");
            String title = resultSet.getString("bp.title");
            String content = resultSet.getString("bp.content");
            String createdAt = resultSet.getTimestamp("bp.created_at").toString();
            String author = resultSet.getString("u.username");
            int likeCount = resultSet.getInt("like_count");

            System.out.printf("文章ID:%d,标题:%s,作者:%s,发布时间:%s,点赞数:%d%n", 
                             postId, title, author, createdAt, likeCount);
            System.out.println("内容:" + content);
            System.out.println("-----------------------------------");
        }

    } catch (SQLException e) {
        System.out.println("查看文章出问题啦:" + e.getMessage());
    }
}

// 修改主菜单,添加新选项
private static void showBlogSystemMenu(Scanner scanner, int userId) {
    while (true) {
        System.out.println("\n博客系统主菜单:");
        System.out.println("1. 发表新文章");
        System.out.println("2. 查看所有文章");
        System.out.println("3. 评论文章");
        System.out.println("4. 给文章添加标签");
        System.out.println("5. 按标签搜索文章");
        System.out.println("6. 点赞文章");
        System.out.println("7. 查看带点赞数的文章列表");
        System.out.println("8. 退出登录");
        System.out.print("请选择:");

        int choice = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符

        if (choice == 1) {
            publishNewPost(scanner, userId);
        } else if (choice == 2) {
            viewAllPosts(scanner);
        } else if (choice == 3) {
            commentOnPost(scanner, userId);
        } else if (choice == 4) {
            addTagsToPost(scanner, userId);
        } else if (choice == 5) {
            searchPostsByTag(scanner);
        } else if (choice == 6) {
            likePost(scanner, userId);
        } else if (choice == 7) {
            viewPosts_withLikes(scanner);
        } else if (choice == 8) {
            System.out.println("退出登录,欢迎下次再来!");
            break;
        } else {
            System.out.println("别乱选,没这选项!");
        }
    }
}

总结

今天咱这博客系统算是搭了个雏形,从用户注册登录,到发表文章、评论、添加标签、点赞,基本功能都有了。在这个过程中,咱也把 Java 数据库操作的方方面面都实践了一遍:

  1. JDBC 基础:学会了怎么连接数据库,查询、插入、更新、删除数据,这是玩数据库的看家本领。

  2. PreparedStatement:明白了用它能防 SQL 注入,写代码也更规范,就像给 SQL 戴上了安全帽。

  3. 连接池:知道了连接池的好,用 HikariCP 优化了数据库连接管理,让系统能扛住更多用户访问。

  4. 事务管理:在关键操作(比如点赞、评论)中用事务保证数据一致性,这在金融、电商系统里更是关键。

  5. 数据库设计:从最开始设计用户表、文章表、评论表,到后来扩展标签表、点赞表,体会了数据库设计的灵活性和扩展性。

实际开发中,咱还会用到很多高级技巧和框架,比如用 MyBatis 或 Hibernate 这样的 ORM 框架简化代码,用 Redis 缓存热点数据减轻数据库压力,用 Elasticsearch 做全文搜索提升用户体验。但万变不离其宗,今天学的这些基础,就像盖楼打下的地基,后面不管盖多高,这基础都得扎实。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐