JavaWeb-JDBC&事务回滚

什么是JDBC?

Java连接数据库!

需要jar包支持:

  • java.sql
  • javax.sql
  • mysql-connter-java......连接驱动(必须要导入)

实验-Mysql数据库

MySQL数据插入表中数据

sql 复制代码
CREATE TABLE users(
	`id` INT PRIMARY KEY,
	`name` VARCHAR(40),
	`password` VARCHAR(40),
	`email` VARCHAR(2222),
	`birthday` DATE
);

INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)
VALUES(1,'admin','123456','admin@gmail.com','1998-06-18'); 
INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)
VALUES(2,'system','000000','system@gmail.com','1999-06-18'); 
INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)
VALUES(3,'sa','00000000','sa@gmail.com','1992-06-18'); 
INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)
VALUES(4,'web','0123456789','web@gmail.com','1991-06-18'); 


SELECT * FROM users;

导入数据库依赖

xml 复制代码
    <dependencies>
        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
    </dependencies>

idea连接数据库

jdbc连接七部曲

  1. 获取配置信息&解决中文乱码
  2. 加载驱动

显式加载:

复制代码
    * Class.forName("..."):这是最经典的方式,在代码中显式地加载驱动类。例如,Class.forName("com.mysql.cj.jdbc.Driver")。
    * 优点:兼容老版本,建议写、兼容性强。
    * 缺点:需要手动编写代码,在一些情况下(如 JDBC 4.0 之后),可能显得冗余。

隐示加载:

spl模式

复制代码
    * SPI ,全称为 Service Provider Interface,是一种服务发现机制。
    * 它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
    * 这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

jdbc4.0后 可以自动扫描jar包下面的这个文件

  1. 连接数据库,代表数据库
  2. 向数据库发送SQL的对象 Statement 用它来做(CRUD)增删改查
  3. 编写SQL
  4. 执行查询SQL语句
  5. 关闭连接,释放资源

增删改查

java 复制代码
        //受影响的行数,CRUD都是用executeUpdate即可
        int i = preparedStatement.executeUpdate();

完整代码

java 复制代码
public static void main(String[] args) throws ClassNotFoundException, SQLException {
        
        //1.获取配置信息
        // serverTimezone=UTC 解决中文乱码
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF-8";
        String user = "root";
        String password = "123456";

        // 2.加载驱动
//        Class.forName("com.mysql.jdbc.Driver");

        // 3.连接数据库,代表数据库
        Connection connection = DriverManager.getConnection(url, user, password);

        // 4.向数据库发送SQL的对象,Statement :用它来做(CRUD)增删改查
        Statement statement = connection.createStatement();

        // 5.编写SQL
        String sql = "select * from users;";

        // 6.执行查询SQL语句,返回一个ResultSet对象
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()){// 如果还有数据
            System.out.println("id="+resultSet.getObject("id"));
            System.out.println("name="+resultSet.getObject("name"));
            System.out.println("password="+resultSet.getObject("password"));
            System.out.println("email="+resultSet.getObject("email"));
            System.out.println("birthday="+resultSet.getObject("birthday"));
        }
        // 7.关闭连接,释放资源(一定要做)先开后关
        statement.close();
        connection.close();
        resultSet.close();
    }


}

预编译SQL

更加安全

java 复制代码
public class TestJdbc2 {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1.配置信息
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF-8";
        String user = "root";
        String password = "123456";

        // 2.加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 3.连接数据库
        Connection connection = DriverManager.getConnection(url, user, password);

        // 4.编写SQL
        String sql = "insert into users(`id`,`name`,`email`,`password`,`birthday`) values (?,?,?,?,?);";

        // 5.预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置?的值
        preparedStatement.setInt(1,5); //给第一个占位符,赋值id为5
        preparedStatement.setString(2,"test"); //给第二个占位符,赋值name为test
        preparedStatement.setString(3,"test@gmail.com"); //给第三个占位符,赋值email为test@gmail.com
        preparedStatement.setString(4,"123456"); //给第四个占位符,赋值password为123456
        preparedStatement.setDate(5,new Date(new java.util.Date().getTime())); //给第五个占位符,赋值birthday为当前时间


        // 5. 执行SQL
        int i = preparedStatement.executeUpdate();
        //增删改查的判断
        if (i > 0){
            System.out.println("插入成功");
        }else{
            System.out.println("插入失败");
        }

        // 6.关闭连接,释放资源
        connection.close();
        preparedStatement.close();
    }

}

事务

要么都成功,要么都失败

ACID原则(原子性、一致性、隔离性、持久性):保证数据的安全。

  • 开启事务
  • 事务提交 commit()
  • 事务回滚 rollback()
  • 关闭事务

转账:

A:1000

B:1000

A(900) --100-->B(1100)

如果服务器崩溃了怎么办?我们不应该让这100块钱凭空的消失

Junit单元测试

测试数据经常使用,不需要new类什么的

导入依赖

pom.xml

xml 复制代码
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
java 复制代码
public class TestJdbc3 {
    @Test
    public void test(){
        System.out.println("test");
    }

}

添加@Test注解

可以看见运行键不再是灰色了,可以直接输出

@Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!

事务

常用通常有以下:

  • start transaction ;
  • rollback ;
  • commit ;
sql 复制代码
start transaction ; #开启事务

update jdbc.account set money = money- 100  where id = '1';
# update jdbc.account set money = money+ 100  where id = '1';
rollback ; # 回滚事务,如果中间崩溃了怎么样,就会回滚,不进行提交
commit ; # 提交事务

可以选中这两行执行

就会只执行这两行

回滚

一定要添加这一行,这个引擎支持回滚

sql 复制代码
ALTER TABLE jdbc.account ENGINE=InnoDB;

案例代码:

java 复制代码
public class TestJdbc3 {

    @Test
    public void test() {
        //配置信息
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF-8";
        String user = "root";
        String password = "123456";

        Connection connection = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 连接数据库
            connection = DriverManager.getConnection(url, user, password);

            // 检查自动提交状态
            System.out.println("自动提交状态: " + connection.getAutoCommit());

            // 开启事务
            connection.setAutoCommit(false);
            System.out.println("事务已开启");

            // 执行第一条SQL
            String sql = "update jdbc.account set money = money - 100 where id = 2;";
            PreparedStatement pstmt1 = connection.prepareStatement(sql);
            int count1 = pstmt1.executeUpdate();
            System.out.println("第一条SQL影响行数: " + count1);
            pstmt1.close();

            // 检查当前数据状态
            checkAccountBalance(connection);

            // 制造错误 - 这会触发回滚
            int i = 1/0;

            // 执行第二条SQL
            String sql1 = "update jdbc.account set money = money + 100 where id = 3;";
            PreparedStatement pstmt2 = connection.prepareStatement(sql1);
            int count2 = pstmt2.executeUpdate();
            System.out.println("第二条SQL影响行数: " + count2);
            pstmt2.close();

            // 提交事务
            connection.commit();
            System.out.println("事务提交成功!");

        } catch (Exception e) {
            System.out.println("捕获到异常: " + e.getMessage());

            // 发生异常时回滚事务
            if (connection != null) {
                try {
                    connection.rollback();
                    System.out.println("事务已回滚!所有操作已撤销");

                    // 回滚后再次检查数据状态
                    checkAccountBalance(connection);
                } catch (SQLException ex) {
                    System.out.println("回滚失败: " + ex.getMessage());
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 恢复自动提交并关闭连接
            if (connection != null) {
                try {
                    connection.setAutoCommit(true); // 恢复自动提交
                    connection.close();
                    System.out.println("连接已关闭");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 辅助方法:检查账户余额
    private void checkAccountBalance(Connection conn) throws SQLException {
        String checkSql = "SELECT id, money FROM jdbc.account WHERE id IN (2, 3)";
        PreparedStatement pstmt = conn.prepareStatement(checkSql);
        ResultSet rs = pstmt.executeQuery();

        System.out.println("当前账户余额:");
        while (rs.next()) {
            System.out.println("账户 " + rs.getInt("id") + ": " + rs.getDouble("money"));
        }

        rs.close();
        pstmt.close();
    }
}
相关推荐
青啊青斯2 小时前
python markdown转word【包括字体指定】
开发语言·python·word
corpse20102 小时前
trae下载依赖包特别慢!!!
开发语言·python
rainFFrain2 小时前
QT显示类控件---QSlider
开发语言·qt
dragoooon342 小时前
[C++——lesson30.数据结构进阶——「红黑树」]
开发语言·数据结构·c++
云泽8082 小时前
C++ STL 栈与队列完全指南:从容器使用到算法实现
开发语言·c++·算法
郑州光合科技余经理2 小时前
实战:攻克海外版同城生活服务平台开发五大挑战
java·开发语言·javascript·数据库·git·php·生活
蟹至之2 小时前
【MySQL】JDBC的使用(万字解析)
java·数据库·mysql·jdbc
爱笑的眼睛112 小时前
超越翻转与裁剪:面向生产级AI的数据增强深度实践与多模态演进
java·人工智能·python·ai
长孙阮柯2 小时前
Java进阶篇(五)
java·开发语言