当前位置: 首页 > news >正文

一天学完JDBC!!(万字总结)

文章目录

    • JDBC是什么
  • 1、环境搭建 && 入门案例
  • 2、核心API理解
    • ①、注册驱动(Driver类)
    • ②、Connection
    • ③、statement(sql注入)
    • ④、PreparedStatement
    • ⑤、ResultSet
  • 3、jdbc扩展(ORM、批量操作)
    • ①、实体类和ORM
    • ②、批量操作
  • 4. 连接池
    • ①、常用连接池
    • ②、Durid连接池
    • ③、HikariCP连接池
  • 5、JDBC工具类
    • ①、JDBC工具类初步封装
    • ②、加强工具类(引入ThreadLocal)
  • 6、BaseDAO详解
    • ①、BaseDAO的概念&&搭建
    • ②、BaseDAO搭建增删改查
  • 7、JDBC事务

JDBC是什么

JDBC(Java Database Connectivity),意为java数据库连接。

  • jdbc是java提供的一组独立于任何数据库管理系统的api
  • java提供接口规范,由各个厂商提供接口的实现,厂商提供的实现类封装成jar文件,也就是俗称的数据库驱动jar包。
  • JDBC的好处是,程序员只关心标准和规范,无需关注实现过程。

在这里插入图片描述

1、环境搭建 && 入门案例

开发环境:
Mysql 8.0.23 + idea2022 + jdk17 + maven3.8.1

创建数据库语句:

CREATE DATABASE test_jdbc;USE test_jdbc;CREATE TABLE t_emp
(emp_id    INT AUTO_INCREMENT COMMENT '员工编号' PRIMARY KEY,emp_name  VARCHAR(100) NOT NULL COMMENT '员工姓名',emp_salary DOUBLE(10, 5) NOT NULL COMMENT '员工薪资',emp_age   INT NOT NULL COMMENT '员工年龄'
);INSERT INTO t_emp (emp_name, emp_salary, emp_age)
VALUES ('张三', 777.77, 32),('李四', 666.66, 41),('王五', 111, 23),('赵六', 123, 26),('钱七', 123, 28);

创建java项目:
在这里插入图片描述
添加pom.xml:

	<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency></dependencies>

项目结构:

在这里插入图片描述

编写入门案例:

    public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2. 获取连接对象String url = "jdbc:mysql://localhost:3306/test_jdbc"; //连接urlString userName = "root"; // 用户名String password = "root"; // 密码Connection connection = DriverManager.getConnection(url, userName, password);//3. 获取执行sql语句的对象Statement statement = connection.createStatement();//4. 编写sql语句并执行,以及接收返回的结果集String sql = "SELECT * FROM t_emp";ResultSet resultSet = statement.executeQuery(sql);//5. 处理结果,遍历resultSet结果集while (resultSet.next()) {int empId = resultSet.getInt("emp_id");String empName = resultSet.getString("emp_name");double empSalary = resultSet.getDouble("emp_salary");int empAge = resultSet.getInt("emp_age");System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);}// 6.关闭资源(先开后关原则)resultSet.close();statement.close();connection.close();}

运行结果:

1	张三	777.77	32
2	李四	666.66	41
3	王五	111.0	23
4	赵六	123.0	26
5	钱七	123.0	28

2、核心API理解

①、注册驱动(Driver类)

在这里插入图片描述
类中最关键代码是是:

DriverManager.registerDriver(new Driver());

同样我们在正式代码中也可以改用这行代码来注册驱动。

在java6以后,我们不需要注册驱动,jdk也会自动检查我们是否存在相关jar包,有没有导入,如果有,自动帮我们加载了。该文件与被实现类的名字相同,而里面存的是我们要加载的驱动类,这样jdk就实现了自动加载驱动的功能。
在这里插入图片描述

②、Connection

  • Connection接口是JDBC API的重要接口,用于与数据库建立的通信通道。换而言之,Connection对象不为null,则代表一次数据库连接。

  • 在建立连接时,需要指定数据库URL、用户名、密码参数。

    • URL: jdbc:mysql://localhost:3306/atguigu
      • jdbc:mysql://[IP地址:端口号/数据库名称?参数数值对1&参数数值对2]
  • Connection 接口还需要管理事务,Connection 接U提供 commit 和 rollback 方法,用于提交事务和回滚事务。

  • 可以创建 Statement 对象,用于执行 SQL 查询与半数据操作交互。

  • 在使用JDBC技术时,必须要先获取Connection对象,在使用完毕后,要释放资源,避免资源占用用很费资源。

③、statement(sql注入)

  • Statement 接口用于执行 SQL 查询并与数据库进行交互。它是 JDBC API 中的一个重要接口。通过 Statement 对象,可以向数据库发送 SQL 查询并获取执行结果。

  • 结果可以是一个或多个结果。

    • 增删改: 变更影响行数单个结果。
    • 查询: 单行单列,多行多列,单行多列等结果。
  • 但是 Statement 接口在执行SQL查询时,会产生 SQL 注入攻击问题:

    • 当使用 Statement 执行动态拼接的 SQL 查询时,往往需要将查询条件与 SQL 查询拼接在一起,直接将 参数和SQL查询一并生成,拼接成SQL语句的条件将为true得到结果。

sql注入演示:

    public static void main(String[] args) throws Exception {// 1.获取连接对象Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_jdbc", "root", "root");// 2.获取执行sql语句的对象Statement statement = connection.createStatement();Scanner sc = new Scanner(System.in);String name = sc.nextLine();// 3. 执行sql语句String sql = "select * from t_emp where emp_name = '" + name + "'";ResultSet resultSet = statement.executeQuery(sql);while (resultSet.next()) {int empId = resultSet.getInt("emp_id");String empName = resultSet.getString("emp_name");double empSalary = resultSet.getDouble("emp_salary");int empAge = resultSet.getInt("emp_age");System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);}// 6.关闭资源(先开后关原则)resultSet.close();statement.close();connection.close();}

想要的效果:

请输入员工姓名:
张三
1	张三	777.77	32

sql注入:

请输入员工姓名:
abc' or '1' = '1
1	张三	777.77	32
2	李四	666.66	41
3	王五	111.0	23
4	赵六	123.0	26
5	钱七	123.0	28

sql注入其实就是动态拼接sql语句会导致语句实际效果和实际想要的不一样,这里是简单演示。

④、PreparedStatement

  • PreparedStatement 是 Statement 接口的子接口,用于执行 预编译 的 SQL 查询,作用和优点:

    • 预编译SQL语句:在创建PreparedStatement时,就会对预编译SQL语句, 也就是说SQL语句已经固定。

    • 防止SQL注入:PreparedStatement 支持参数化查询,将数据作为参数传递到SQL语句中,采用?占位符 的方式,将传入的参数用一对单引号包裏起来”,天龙你怎么计算存的价值。有数据的非法注入关難字或 SQL注入问题。

    • 性能提升:PreparedStatement是预编译SQL语句,同一SQL语句多次执行的情况下,可以复用,不必每次 重新编译和解析。

  • 后续的功能都是基于PreparedStatement执行实现,更安全。更效率更高。

PreparedStatement不支持无参创建,因此创建的时候必须传入一个sql。

    public static void main(String[] args) throws Exception {// 1.获取连接对象Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_jdbc", "root", "root");// 2.获取执行sql语句的对象String sql = "select * from t_emp where emp_name = ?";PreparedStatement statement = connection.prepareStatement(sql);System.out.println("请输入员工姓名:");Scanner sc = new Scanner(System.in);String name = sc.nextLine();// 3. 为?占位符赋值, 并执行sql语句statement.setString(1, name); //这里第一个参数是指的占位符的位置ResultSet resultSet = statement.executeQuery();while (resultSet.next()) {int empId = resultSet.getInt("emp_id");String empName = resultSet.getString("emp_name");double empSalary = resultSet.getDouble("emp_salary");int empAge = resultSet.getInt("emp_age");System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);}// 6.关闭资源(先开后关原则)resultSet.close();statement.close();connection.close();}

它之所以能防止sql注入,就是因为无论你传入什么值,它都会在外面给你加一个’ ',这样无论你里面怎么写,都不会影响到整个sql的条件。

⑤、ResultSet

  • ResultSet 是 JDBC API 中的一个接口,用于表示从数据库中 执行查询语句后返回的结果集。它提供了一种用 于遍历和访问查询结果的方式。

  • 遍历结果:ResultSet可以通过使用 next() 方法将指针移动到结果集集的下一行,逐行遍历数据库查询的结果,返 回值为boolean类型,true代表有下一行结果,false则代表没有。

  • 提取单列结果:可以通过 getXXX的方式来获取单列的数据,该方法为重载方法,支持索引和列名进行获取。
    next()用于判断有没有下一行。
    在这里插入图片描述

3、jdbc扩展(ORM、批量操作)

①、实体类和ORM

实体类:

  • 在使用JDBC操作数据库时,我们会发现数据都是零散的, 明明在数据库中是一行完整的数据,到了Java 中变成了一个一个的变量,不利于维护和管理。 所以我们要把数据库存在一个载体里, 这 个载体就是实体类!

  • ORM(Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概 念跟数据库中表的概念对应起来,以面向对象的方式操作数据库中数据,即一张表对应一个类,一个对象对应一条数据,一个类对应一个属性!

  • 当下JDBC中运用和过程为手动ORM。现在已经有ORM框架,比如MyBatis等。

//类名和表要映射库名对应,但是表名一般都有前缀,类名只要全写! 
public class Employee {private Integer empId;//emp_id = empId 数据库中列名用下划线分隔,属性名用驼峰! private String empName;//emp_name = empNameprivate Double empSalary;//emp_salary = empSalaryprivate Integer empAge;//emp_age = empAge//省略get、set、构造、有参、toString方法。
}

ORM思想:

Object Relation Mapping思想,即对象到关系数据库的映射。

封装单个对象:

@Testpublic void testORM() throws Exception {// 1.获取连接对象Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_jdbc", "root", "root");// 2.获取执行sql语句的对象String sql = "select * from t_emp where emp_id = ?";PreparedStatement statement = connection.prepareStatement(sql);statement.setInt(1, 1);ResultSet resultSet = statement.executeQuery();Employee employee = null;while (resultSet.next()) {employee = new Employee();int empId = resultSet.getInt("emp_id");String empName = resultSet.getString("emp_name");double empSalary = resultSet.getDouble("emp_salary");int empAge = resultSet.getInt("emp_age");employee.setEmpAge(empAge);employee.setEmpId(empId);employee.setEmpName(empName);employee.setEmpSalary(empSalary);}System.out.println(employee);resultSet.close();statement.close();connection.close();}

封装集合:

List<Employee> list = new ArrayList<>();

new一个集合,然后每次执行完之后放到集合中,打印即可。

②、批量操作

  • 插入多条数据的时候,一条一条发送的效率太低,因此引入批量操作提高效率
        String sql = "insert into t_emp (emp_name, emp_salary, emp_age) values (?, ?, ?)";PreparedStatement statement = connection.prepareStatement(sql);for(int i = 1; i < 1000; i ++ ) {statement.setString(1, "marry" + i);statement.setDouble(2, 100.0 + i);statement.setInt(3, 20 + i);statement.executeUpdate();}

这样的效率相对较低
连接url中添加参数:

"jdbc:mysql://localhost:3306/test_jdbc?rewriteBatchedStatements=true"

批量优化:

        for(int i = 1; i < 1000; i ++ ) {statement.setString(1, "marry" + i);statement.setDouble(2, 100.0 + i);statement.setInt(3, 20 + i);statement.addBatch();}statement.executeBatch();

这样会减少与mysql交互的次数。

4. 连接池

  • 连接池就是数据库连接对象的数据仓库,通过配置,由连接池负责创建连接、管理连接、释放连接等操作。

  • 预先创建数据库连接放入连接池,用户在请求时,通连接池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了解决了创建的效率。

  • 当池中无连接可用,且未达到上限时,连接池会新建连接。

  • 池中连接达到上限,用户请求会等待,可以设置超时时问。

①、常用连接池

JDBC 的数据库连接池使用 javax.sql.DataSource接口进行规范,所有有的第三方连接池实现此接口!也就是说,使用连接池获取连接的和回收连接方式都一样,不同的只是性能和扩展功能!

也就是说,使用连接池获取连接的和回收连接方式都一样,不同的只是性能和扩展功能。

我们只看两个主要的连接池:

  • Druid 是阿里提供快的数据库连接池,是集DBCP、C3P0、Proxool 优点于一身的数据库连接池,性能、扩展 性、易用性都更好,功能丰富。

  • Hikari(ひかり [shi ga li])取自日语,是光的意思,是SpringBoot2.x.x之后内置的一款连接池,基于 BoneCP(已经放弃维护,推荐该连接池)做了不少的改进和优化,日志量被极度精简,可靠性很高。

②、Durid连接池

引入依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version>
</dependency>

使用方法(软编码):
创建:resource/db.properties

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test_jdbc
username=root
password=root
initialSize=10
maxActive=20

测试代码:

    @Testpublic void testDruid() throws Exception {Properties properties = new Properties();InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");properties.load(is);DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}

③、HikariCP连接池

引入pom.xml:

<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.0.1</version>
</dependency>

其余步骤都相同,核心代码只改变这几行:

//3. 创建HikariConfig连接池配置对象,将Properties集合传递进去。
HikariConfig hikariConfig = new HikariConfig(properties);//4. 基于HikariConfig连接池配置对象,创建HikariDataSource
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);//5. 获取连接
Connection connection = hikariDataSource.getConnection();
System.out.println(connection);

5、JDBC工具类

①、JDBC工具类初步封装

db.properties :

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test_jdbc
username=root
password=root
initialSize=10
maxActive=20

工具类代码:

public class JDBCUtil {// 创建连接池引用,提供全局使用private static DataSource dataSource;// 项目创建时就创建连接池对象并赋值static {try {Properties properties = new Properties();InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");properties.load(is);dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {throw new RuntimeException(e);}}//获取连接的方法public static Connection getConnection() {try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}}// 回收连接的方法public static void release(Connection connection) {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}
}

测试代码:

Connection connection = JDBCUtil.getConnection();
System.out.println(connection);JDBCUtil.release(connection);

可以看到,我们将重复的创建步骤都封装到一个工具类之后,写正式测试主函数的时候发现代码就少了很多。

②、加强工具类(引入ThreadLocal)

ThreadLocal用于保存每个线程共享变量,原因是在Java中,每一个线程对象中都有一个

ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。

而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,

set,remove自己的变量,而不会影响其他线程的变量。

在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
线程间数据隔离。

  • 执行事务操作,用于存储线程事务信息。
  • 数据库连接,Session会话管理。
  • ThreadLocal对象.get: 获取ThreadLocal中当前线程共享变量的值。
  • ThreadLocal对象.set: 设置ThreadLocal中当前线程共享变量的值。
  • ThreadLocal对象.remove: 移除ThreadLocal中当前线程共享变量的值。
public class JDBCUtil_v2 {// 创建连接池引用,提供全局使用private static DataSource dataSource;private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();// 项目创建时就创建连接池对象并赋值static {try {Properties properties = new Properties();InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");properties.load(is);dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {throw new RuntimeException(e);}}//获取连接的方法public static Connection getConnection() {try {// 在Thread Local中取连接,如果第一次创建,就先设置进入ThreadLocal。Connection connection = threadLocal.get();if (connection == null) {connection = dataSource.getConnection();threadLocal.set(connection);}return connection;} catch (SQLException e) {throw new RuntimeException(e);}}// 回收连接的方法public static void release() {try {Connection connection = threadLocal.get();if(connection == null) {threadLocal.remove();connection.close();}} catch (SQLException e) {throw new RuntimeException(e);}}
}

测试代码:

@Testpublic void testJDBCV2() {// 有ThreadLocal版本Connection connection = JDBCUtil_v2.getConnection();Connection connection1 = JDBCUtil_v2.getConnection();Connection connection2 = JDBCUtil_v2.getConnection();System.out.println(connection);System.out.println(connection1);System.out.println(connection2);// 无ThreadLocal版本Connection connection3 = JDBCUtil_v1.getConnection();Connection connection4 = JDBCUtil_v1.getConnection();Connection connection5 = JDBCUtil_v1.getConnection();System.out.println(connection3);System.out.println(connection4);System.out.println(connection5);}

运行结果:

com.mysql.cj.jdbc.ConnectionImpl@4b34fff9
com.mysql.cj.jdbc.ConnectionImpl@4b34fff9
com.mysql.cj.jdbc.ConnectionImpl@4b34fff9
com.mysql.cj.jdbc.ConnectionImpl@2a225dd7
com.mysql.cj.jdbc.ConnectionImpl@61eaec38
com.mysql.cj.jdbc.ConnectionImpl@125290e5

可以看到,引入ThreadLocal之后,极大节省了资源,一个连接可以多次复用。

6、BaseDAO详解

①、BaseDAO的概念&&搭建

  • DAO :Data Access Object 数据访问对象
    可以理解为对一张表的增删改查操作都维护在一个对应的DAO中。
  • BaseDAO:抽出公共性代码,创建一个公共的父类,给这些DAO的实现类可以抽出公共性代码,复用简单更改的基础操作,我们称为BaseDAO。

搭建DAO:
在这里插入图片描述
EmployeeDAO接口:

public interface EmployeeDao {List<Employee> selectAll();Employee selectByEmpId(Integer empId);int insert(Employee employee);int update(Employee employee);int deleteById(Integer empId);}

实现类:

public class EmployeeDaoImpl implements EmployeeDao {@Overridepublic List<Employee> selectAll() {return null;}@Overridepublic Employee selectByEmpId(Integer empId) {return null;}@Overridepublic int insert(Employee employee) {return 0;}@Overridepublic int update(Employee employee) {return 0;}@Overridepublic int deleteById(Integer empId) {return 0;}
}

②、BaseDAO搭建增删改查

每个增删改方法执行前,都需要执行下面这六步操作,而BaseDAO其实就是将这些重复步骤抽离出来。1、2、6步骤在工具类中已经封装好了,而BaseDAO其实就是处理中间三步的过程。

		//1.注册驱动//2.获取连接//3.预编译sql语句//4.为占位符赋值,执行sql,接收返回结果//5.处理结果//6.释放资源
public class BaseDAO {/*** 通用增删改方法,返回结果均为一个int整数,表示被影响的行数** @param sql    sql语句* @param params 给占位符要赋值的参数值。* @return* @throws Exception*/public int executeUpdate(String sql, Object... params) throws Exception {Connection connection = JDBCUtil_v2.getConnection();//3.预编译sql语句PreparedStatement preparedStatement = connection.prepareStatement(sql);//4.为占位符赋值,执行sql,接收返回结果if (params == null && params.length > 0) {for (int i = 0; i < params.length; i++) {preparedStatement.setObject(i + 1, params[i]);}}int row = preparedStatement.executeUpdate();//6.释放资源JDBCUtil_v2.release();//5.处理结果return row;}/*** 通用查询方法,返回结果为一个泛型集合,通过反射实现多行多列、单行多列、单行单列查询** @param clazz* @param sql* @param params* @param <T>* @return* @throws Exception*/public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws Exception {Connection connection = JDBCUtil_v2.getConnection();//3.预编译sql语句PreparedStatement preparedStatement = connection.prepareStatement(sql);//4.为占位符赋值,执行sql,接收返回结果if (params == null && params.length > 0) {for (int i = 0; i < params.length; i++) {preparedStatement.setObject(i + 1, params[i]);}}ResultSet resultSet = preparedStatement.executeQuery();//获取结果集中的元数据对象//包含了:列的数量 和 每个列的名称ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();List<T> list = new ArrayList<>();//5.处理结果while (resultSet.next()) {// 通过反射创建一个实例T t = clazz.newInstance();// 循环遍历当前列,循环几次for (int i = 1; i <= columnCount; i++) {//通过下标获取列的值Object value = resultSet.getObject(i);//通过下标获取列的名字String fieldName = metaData.getColumnLabel(i);Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);field.set(t, value);}list.add(t);}resultSet.close();preparedStatement.close();//6.释放资源JDBCUtil_v2.release();return list;}/*** 单个查询,在上面集合基础上进行单个查询的优化* @param clazz* @param sql* @param params* @return* @param <T>* @throws Exception*/public <T> T executeQueryBean(Class<T> clazz, String sql, Object... params) throws Exception {List<T> list = this.executeQuery(clazz, sql, params);if (list == null) {return null;}return list.get(0);}}

EmployeeDaoImpl中调用BaseDAO实现代码:

public class EmployeeDaoImpl extends BaseDAO implements EmployeeDao {@Overridepublic List<Employee> selectAll() {try {String sql = "select emp_id empId, emp_name empName, emp_salary empSalary, emp_age empAge from t_emp";return executeQuery(Employee.class, sql, null);} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic Employee selectByEmpId(Integer empId) {try {String sql = "select emp_id empId, emp_name empName, emp_salary empSalary, emp_age empAge from t_emp where emp_id = ?";return executeQueryBean(Employee.class, sql, 1);} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic int insert(Employee employee) {try {String sql = "insert into t_emp (emp_name, emp_salary, emp_age) values (?, ?, ?)";return executeUpdate(sql, employee.getEmpName(), employee.getEmpSalary(), employee.getEmpAge());} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic int update(Employee employee) {try {String sql = "update t_emp set emp_salary = ? where emp_id = ?";return executeUpdate(sql, employee.getEmpSalary(), employee.getEmpId());} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic int deleteById(Integer empId) {try {String sql = "delete from t_emp where emp_id = ?";return executeUpdate(sql, empId);} catch (Exception e) {throw new RuntimeException(e);}}
}

测试类代码:

@Testpublic void testEmployeeDAO() {EmployeeDao employeeDao = new EmployeeDaoImpl();// 1. 多个查询List<Employee> employees = employeeDao.selectAll();System.out.println(employees);// 2. 单个查询Employee employee0 = employeeDao.selectByEmpId(1);System.out.println(employee0);// 3.插入操作Employee employee = new Employee(null, "tom", 300.65, 38);int row = employeeDao.insert(employee);System.out.println(row);// 4.更新操作Employee employee2 = new Employee(10, "tom", 10000.0, 38);int row2 = employeeDao.update(employee2);System.out.println(row2);//5.删除操作int row3 = employeeDao.deleteById(10);System.out.println(row3);}

运行结果:

[Employee{empId=1, empName='张三', empSalary=777.77, empAge=32}, Employee{empId=2, empName='李四', empSalary=666.66, empAge=41}, Employee{empId=3, empName='王五', empSalary=111.0, empAge=23}, Employee{empId=4, empName='赵六', empSalary=123.0, empAge=26}, Employee{empId=5, empName='钱七', empSalary=123.0, empAge=28}]
Employee{empId=1, empName='张三', empSalary=777.77, empAge=32}
1
1
1

7、JDBC事务

数据库事务就是一串SQL语句执行的整体机制,不会单条执行完更新数据库更新数据,最终根据最终存内的参数
语向执行结果统一处理! 一个事务内所有语句都成功及事务成功,我们可以通过提交commit提交事务结果给事务
务,更新数据! 一个事务内任意一条语句的失败,即为事务失败,我们可以通过触发rollback回滚结果事务,数据回到事务之前状态!

public class JDBCTransactionDemo {public static void main(String[] args) {try (Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbc_test", "root", "root")) {conn.setAutoCommit(false); // 开始手动事务// 插入表1PreparedStatement ps1 = conn.prepareStatement("INSERT INTO table1 (col1) VALUES (?)");ps1.setString(1, "data1");ps1.executeUpdate();// 插入表2(假设这里会失败,例如表不存在)PreparedStatement ps2 = conn.prepareStatement("INSERT INTO table2 (col2) VALUES (?)");ps2.setString(1, "data2");ps2.executeUpdate();conn.commit(); // 提交事务System.out.println("Transaction committed.");} catch (SQLException e) {// 回滚处理try{conn.rollback();} catch(Exception e) {throw new RuntimeException(e);}}}
}

相关文章:

一天学完JDBC!!(万字总结)

文章目录 JDBC是什么 1、环境搭建 && 入门案例2、核心API理解①、注册驱动(Driver类)②、Connection③、statement(sql注入)④、PreparedStatement⑤、ResultSet 3、jdbc扩展(ORM、批量操作)①、实体类和ORM②、批量操作 4. 连接池①、常用连接池②、Durid连接池③、Hi…...

【愚公系列】《Manus极简入门》011-习惯养成教练:“习惯塑造师”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…...

精益数据分析(38/126):SaaS模式的流失率计算优化与定价策略案例

精益数据分析&#xff08;38/126&#xff09;&#xff1a;SaaS模式的流失率计算优化与定价策略案例 在创业和数据分析的领域中&#xff0c;我们不断探索如何更精准地把握业务发展的关键要素。今天&#xff0c;带着与大家共同进步的想法&#xff0c;深入研读《精益数据分析》&a…...

50.【必备】二分答案法与相关题目

本文的网课内容学习自B站左程云老师的算法详解课程&#xff0c;旨在对其中的知识进行整理和分享~ 网课链接&#xff1a;算法讲解051【必备】二分答案法与相关题目_哔哩哔哩_bilibili 一.爱吃香蕉的珂珂 题目&#xff1a;爱吃香蕉的珂珂 算法原理 整体思路 这是一个二分查找算法…...

C# 方法(局部变量和局部常量)

本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 局部变量 和第5章介绍的字段…...

MQTT 协议与 HTTP 协议的区别

在现代的网络通信中&#xff0c;MQTT 协议和 HTTP 协议都扮演着重要的角色&#xff0c;但它们有着不同的特点和适用场景。下面我们就从多个方面来详细探讨它们之间的区别。 一.协议设计理念 1. MQTT 协议 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;即…...

博弈论思维——AI与思维模型【90】

一、定义 博弈论思维模型是一种研究在相互影响的决策情境中&#xff0c;参与者如何通过策略选择来实现自身利益最大化的理论框架。它分析参与者之间的相互作用、策略组合以及由此产生的结果&#xff0c;帮助人们理解在竞争或合作环境下的决策逻辑和行为模式。 二、由来 博弈…...

【Bootstrap V4系列】学习入门教程之 表格(Tables)和画像(Figure)

Bootstrap V4系列 学习入门教程之 表格&#xff08;Tables&#xff09;和画像&#xff08;Figure&#xff09; 表格&#xff08;Tables&#xff09;一、Examples二、Table head options 表格头选项三、Striped rows 条纹行四、Bordered table 带边框的表格五、Borderless table…...

第 3 篇:有序的世界:有序表 (TreeMap/TreeSet) 的概念与优势

上一篇我们探讨了哈希表如何以牺牲顺序为代价换取极致的平均速度。然而&#xff0c;在现实世界的许多应用中&#xff0c;数据的有序性不仅是锦上添花&#xff0c;甚至是核心需求。想象一下&#xff1a; 你需要显示一个按价格排序的商品列表。你需要找到某个时间点之前或之后的…...

VulnHub-DC-2靶机

主机发现 sudo arp-scan -l 以sudo管理员权限扫描本地活动ip地址 Interface: eth0, type: EN10MB, MAC: 08:00:27:22:46:4f, IPv4: 192.168.252.230 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.252.6 4c:5f:70:74:3c:3b …...

论文笔记(八十三)STACKGEN: Generating Stable Structures from Silhouettes via Diffusion

STACKGEN: Generating Stable Structures from Silhouettes via Diffusion 文章概括摘要I. INTRODUCTIONII. 相关工作A. 从直觉物理学学习稳定性B. 用于姿态生成的扩散模型C. 自动化顺序装配 III. 方法A. 用于 S E ( 3 ) SE(3) SE(3)积木姿态生成的扩散模型B. 模型架构C. 数据生…...

论文阅读笔记——TesserAct: Learning 4D Embodied World Models

TesserAct 论文 采用RGB-DN&#xff08;RGB深度法线&#xff09; 作为 4D 场景中间表示&#xff0c;由此建模 4D 场景&#xff0c;比纯 2D 视频更准确地建模 3D 几何结构。相比现有的 4D 视频生成&#xff0c;优化速度快&#xff0c;收敛好&#xff0c;且首次从当前帧和文本描述…...

变转速振动信号分析处理与故障诊断算法模块

变转速振动信号分析处理与故障诊断算法模块&#xff0c;作为信号处理算法工具箱的主要功能模块&#xff0c;形成了以变转速振动信号分析处理与故障诊断算法模块的经典算法模型&#xff0c;可应用于各类关键机械部件&#xff08;轴承、齿轮、转子等&#xff09;的信号分析、故障…...

每日算法-250502

每日算法 - 2025.05.02 记录一下今天刷的几道 LeetCode 算法题。 3191. 使二进制数组全部等于 1 的最少操作次数 I 题目 思路 贪心 解题过程 遍历数组 nums。当我们遇到 nums[i] 时&#xff1a; 如果 nums[i] 是 1&#xff0c;我们不需要进行操作&#xff0c;因为目标是全 …...

如何在纯C中实现类、继承和多态(小白友好版)

基本实现原理 /* 通过结构体函数指针模拟类 */ typedef struct {// 成员变量int x; // 成员方法&#xff08;函数指针&#xff09; void (*print)(void* self); } MyClass;/* 成员函数实现 */ void my_print(void* self) {MyClass* obj (MyClass*)self;p…...

AE/PR插件 转场创建大师专业版 Transition Master Pro v2.0.2 Win+使用教程

Transition Master Pro v2.0.2是一款原生转场插件&#xff0c;专为Adobe Premiere Pro和After Effects设计。它提供了创建、导出和销售自己的转场效果&#xff0c;或从一个庞大的转场预设库中选择。使用Transition Master Pro v2.0.2&#xff0c;您可以快速轻松地创建令人惊叹的…...

[Linux]从零开始的STM32MP157 Buildroot根文件系统构建

一、前言 在前面的教程中&#xff0c;教了大家如何移植一个LInux的内核并且正确启动&#xff0c;我们发现Linux内核在启动后会出现一个错误&#xff0c;提示我们没有找到根文件系统。那么什么是根文件系统呢&#xff1f;之前我们使用Ubuntu编译了STM32MP157的TF-A,UBOOT,LINUX内…...

阿里云服务器 篇五(加更):短链服务网站:添加反垃圾邮件功能

文章目录 系列文章(可选)更新YOURLS版本安装 Compliance 插件安装 Phishtank-2.0 插件(可选)安装 httpBL 插件样例网站(不推荐)使用谷歌解决方案更多系列文章 阿里云服务器 篇一:申请和初始化 阿里云服务器 篇二:搭建静态网站 阿里云服务器 篇三:提交搜索引擎收录 阿…...

状压 DP 详解

文章目录 简介做法洛谷 P1171 简介 状压 DP 其实约等于一个 DP 的小技巧&#xff0c;一般应用在处理一个或多个集合的问题中&#xff08;因为状压 DP 的下标就是一个集合&#xff09;&#xff0c;而且在 n n n 太大的时候建议不要使用这种方法。&#xff08;如果你不懂&#…...

多模态大模型轻量化探索-视觉大模型SAM(Segment Anything Model)

往期&#xff0c;笔者基于LLava的数据对齐训练&#xff0c;搞了一个Reyes多模态大模型&#xff0c;并且看了些多模态大模型&#xff0c;相关开源的多模态大模型如&#xff1a;KimiVL、Internvl、QwenVL等&#xff0c;其视觉编码器的尺寸都比较大&#xff0c;如&#xff1a;Moon…...

数据分析_问题/优化

1 报表开发 1.1 数据问题 (1) 数据易错 问题描述 ①数据整合困难:数据来源多样、格式差异大,整合时处理不当易丢错数据. ②计算逻辑复杂:开发人员对复杂计算逻辑的理解产生偏差,会导致计算结果不准. 解决方案 ①建立数据标准,统一修正字段命名、数据类型、日期格式等 ②加强…...

我的stm32驱动电机驱动着突然就卡死程序死机了是为什么

电源不稳定或干扰 电机启动电流冲击&#xff1a;电机运行时可能导致电源电压跌落&#xff0c;影响STM32稳定性。需检查电源滤波电容、使用独立电源或增加稳压模块 地线干扰&#xff1a;电机与MCU共地时&#xff0c;高频噪声可能通过地线耦合&#xff0c;需采用隔离电路或磁耦芯…...

使用 Java 实现一个简单且高效的任务调度框架

目录 一、任务调度系统概述 &#xff08;一&#xff09;任务调度的目标 &#xff08;二&#xff09;任务调度框架的关键组成 二、任务状态设计 &#xff08;一&#xff09;任务状态流转设计 &#xff08;二&#xff09;任务表设计&#xff08;SQL&#xff09; 三、单机任…...

Git 完整教程:初学者分步指南

大家好&#xff0c;这里是架构资源栈&#xff01;点击上方关注&#xff0c;添加“星标”&#xff0c;一起学习大厂前沿架构&#xff01; Git 是一个分布式版本控制系统&#xff0c;可以帮助开发人员跟踪代码更改、与他人协作以及高效管理软件项目。无论您是初学者还是正在提升…...

数字智慧方案5856丨智慧环保综合解决方案(50页PPT)(文末有下载方式)

资料解读&#xff1a;智慧环保综合解决方案 详细资料请看本解读文章的最后内容。 随着城市化进程的加速和环境问题的日益严峻&#xff0c;智慧环保成为提升城市环境管理水平的重要手段。本文将对智慧环保综合解决方案进行详细解读&#xff0c;探讨其在实际应用中的需求、解决…...

VBA快速合并多列单元格

实例需求&#xff1a;工作表中第3行到第5行有如下图所示的数据表&#xff0c;为了方便展示&#xff0c;隐藏了部分列&#xff0c;实际数据为从C列到DO列。 现需要合并第3行和第4行相同内容的单元格&#xff0c;如第10行到第12行所示。 示例代码如下。 Sub MergeDemo()Dim dicM…...

区块链+IoT:创新场景落地背后的技术攻坚战

物联网&#xff08;IoT&#xff09;与区块链技术作为两大颠覆性技术&#xff0c;正通过深度融合推动各行各业的数字化转型。物联网通过连接海量设备实现数据互通与智能化管理&#xff0c;而区块链凭借去中心化、不可篡改和可追溯的特性&#xff0c;为物联网的安全性、隐私保护和…...

自动化测试项目2 --- 比特纵横 [软件测试实战 Java 篇]

目录 项目介绍 项目源码 库地址 项目功能测试 1. 自动化实施步骤 1.1 编写测试用例 1.2 自动化测试脚本开发 1.2.1 配置相关环境, 添加依赖 1.2.2 代码编写 2. 编写自动化脚本过程问题总结 2.1 Actons 方法的使用 2.2 等待的使用 2.3 页面操作 项目性能测试 1. 进…...

【学习笔记】深入理解Java虚拟机学习笔记——第1章 走进Java

第1章 走进Java 1.1 概述 Java成功的原因 1>一次编写到处运行 2>内存管理安全&#xff0c;自动回收 3>运行时编译 4>强大成熟的第三方库 1.2 Java技术体系 1>Java技术体系组成&#xff1a; -Java语言 -Java虚拟机实现 -class文件格式 -Java类库API -第三方J…...

JavaScript性能优化实战之运行时性能优化

在 JavaScript 开发中,运行时性能优化是确保网页响应迅速和流畅的重要环节。优化运行时性能不仅能提高用户体验,还能在高并发的情况下保证应用的稳定性。本文将细化几个常见的 JavaScript 运行时性能优化策略,帮助你提高代码执行效率。 1️⃣ 避免不必要的内存分配和释放 J…...

走进AI的奇妙世界:探索历史、革命与未来机遇

2022年11月30日&#xff0c;ChatGPT的横空出世像一枚深水炸弹&#xff0c;掀起了全球范围的AI狂潮。但这场革命并非偶然——它背后是80年AI发展史的厚积薄发。从图灵的哲学思辨到深度学习的技术突破&#xff0c;再到生成式AI的“涌现”时刻&#xff0c;AI正以惊人的速度模糊人机…...

用c 编写的笔记搜索程序

{XXX文本记录} 文本记录格式 xxx 搜索词条 #include <stdio.h> #include <string.h> #include <stdlib.h>int main(void){FILE *ffopen("help.txt","r");if(fNULL){perror("file");return -1;}char nr[2000];f…...

鼎讯信通 智能通信干扰设备:多频段多模态信号压制解决方案

在万物互联时代&#xff0c;通信安全已成为现代社会的核心基础设施防护重点。面对日益复杂的电磁环境挑战&#xff0c;新一代智能通信干扰设备通过技术创新实现了信号压制能力的革命性突破。本文将深入解析该设备的八大核心功能与技术特性&#xff0c;展现其在商业通信保障、工…...

软件测试概念

这里写目录标题 需求开发模型软件生命周期瀑布模型螺旋模型增量模型、迭代模型敏捷模型Scrum 测试模型V模型W模型&#xff08;双V模型&#xff09; 需求 用户需求&#xff1a;没有经过合理的评估&#xff0c;通常就是一句话 软件需求&#xff1a;是开发人员和测试人员执行工作…...

数据库性能杀手与调优实践

目录 前言一、索引缺失引发的全表扫描灾难1.现象与影响2.优化策略 二、SELECT * 的隐性成本1.危害分析2.优化实践 三、分页查询的性能陷阱1.深度分页问题2.优化方案对比 四、执行计划分析方法论1.关键指标解读2.典型劣化模式识别 五、综合优化最佳实践总结 前言 在数据库应用开…...

初始化列表详解

1.类中包含以下成员&#xff0c;必须放在初始化列表位置进行初始化&#xff1a; 1. 引用成员变量 2.const成员变量 3. 自定义类型成员(且该类没有默认构造函数时 ) 2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序&#xff0c;与其在初始化列表中的先后次序无关…...

【CVE-2025-1094】:PostgreSQL 14.15 SQL注入漏洞导致的RCE_ 利用代码和分析

目标 PostgreSQL 14.15BeyondTrust Privileged Remote Access (PRA) 和 Remote Support (RS) 软件受影响的版本:使用PostgreSQL 14.15及其版本的BeyondTrust产品Explain CVE-2025-1094 是 PostgreSQL 14.15 版本的 psql 交互式工具中发现的 SQL 注入漏洞。由于输入值的验证不…...

【验证技能】VIP项目大总结

VIP项目快做一段落了&#xff0c;历时一年半&#xff0c;也该要一个大汇总。 VIP简介 VIP开发流程 VIP难点 进程同步 打拍插入不同bit位宽数据问题。 动态升降lane VIP做的不好的地方和改进想法 各层之间交互 testsuite两端关键 所有层的实现架构不统一 VIP经验 ** 架构…...

MyBatis 参数处理全解析

在 Java 开发领域&#xff0c;MyBatis 作为一款优秀的持久层框架&#xff0c;凭借其简洁的设计和强大的功能&#xff0c;受到了广大开发者的青睐。而参数处理作为 MyBatis 中一个至关重要的环节&#xff0c;掌握好它能让我们更高效地使用 MyBatis 进行数据库操作。本文将全面深…...

【自然语言处理与大模型】使用Xtuner进行QLoRA微调实操

本文首先对Xtuner这一微调框架进行简单的介绍。手把手演示如何使用Xtuner对模型进行微调训练&#xff0c;包括数据准备、训练命令执行及训练过程中的监控技巧。最后&#xff0c;在完成微调之后&#xff0c;本文还将介绍如何对微调结果进行简单对话测试。 一、Xtuner微调框架 X…...

2023华为od统一考试B卷【二叉树中序遍历】

前言 博主刷的华为机考题&#xff0c;代码仅供参考&#xff0c;因为没有后台数据&#xff0c;可能有没考虑到的情况 如果感觉对你有帮助&#xff0c;请点点关注点点赞吧&#xff0c;谢谢你&#xff01; 题目描述 思路 0.用Character数组存储树&#xff0c;index下标的左右…...

Midjourney 绘画 + AI 配音:组合玩法打造爆款短视频!

一、引言:AI 重构短视频创作范式 在某短视频工作室的深夜剪辑室里,资深编导正在为一条古风剧情视频发愁:预算有限无法实拍敦煌场景,人工绘制分镜耗时 3 天,配音演员档期排到一周后。而使用 Midjourney 生成敦煌壁画风格的场景图仅需 15 分钟,AI 配音工具实时生成多角色台…...

敏感词 v0.25.1 新特性之返回匹配词,修正 tags 标签

开源项目 敏感词核心 https://github.com/houbb/sensitive-word 敏感词控台 https://github.com/houbb/sensitive-word-admin 版本特性 大家好&#xff0c;我是老马。 敏感词以前在实现的时候&#xff0c;没有返回底层实际匹配的词&#xff0c;有时候问题排查非常耗费时间。 …...

【多线程】六、基于阻塞队列的生产者消费者模型

文章目录 Ⅰ. 生产者消费者模型的概念Ⅱ. 生产者消费者模型的优点Ⅲ. 基于阻塞队列的生产者消费者模型MakefileBlock_queue.hpptask.hpptest.cppⅣ. 如何理解提高了效率❓❓❓Ⅰ. 生产者消费者模型的概念 ​ 生产者消费者模型是一种常见的并发模式,用于解决生产者和消费者之间…...

解决Flutter项目中Gradle构建Running Gradle task ‘assembleDebug‘卡顿问题的终极指南

解决Flutter项目中Gradle构建Running Gradle task ‘assembleDebug‘卡顿问题的终极指南 前言 在开发Flutter应用时,经常会遇到Gradle构建卡在Running Gradle task assembleDebug阶段的问题。本文将分享如何通过配置华为云镜像和使用自定义脚本下载依赖的方法解决这些问题。…...

IntelliJ IDEA 保姆级使用教程

文章目录 一、创建项目二、创建模块三、创建包四、创建类五、编写代码六、运行代码注意 七、IDEA 常见设置1、主题2、字体3、背景色 八、IDEA 常用快捷键九、IDEA 常见操作9.1、类操作9.1.1、删除类文件9.1.2、修改类名称注意 9.2、模块操作9.2.1、修改模块名快速查看 9.2.2、导…...

从此,K8S入门0门槛!

前言 当你想要入门K8S的时候&#xff0c;往往会被各种概念搞的晕乎乎的&#xff0c;什么API Server&#xff0c;Scheduler&#xff0c;Controller manager&#xff0c;Etcd&#xff0c;Pod&#xff0c;Kubelet&#xff0c;kube-proxy&#xff0c;deployment…… 哪怕你使用了…...

vue2和vue3组件如何监听子组件生命周期

在 Vue 中监听子组件的生命周期是一个常见需求&#xff0c;但 Vue 官方并不直接推荐这么做&#xff0c;因为这会打破组件的封装性。但在**一些特定场景&#xff08;如自动化监控、封装逻辑复用&#xff09;**下仍是有意义的。 下面分别讲解 Vue 2 和 Vue 3 中如何监听 子组件的…...

如何用Python绘制两个圆之间的8条公切线

引言 在几何学中&#xff0c;两圆之间存在多种类型的公共切线。本文将通过Python代码演示如何绘制两个同心圆&#xff08;半径分别为1.0和3.0&#xff09;之间的8条公切线&#xff0c;并解释相关数学原理与代码实现细节。 环境准备 import matplotlib.pyplot as plt import …...

会话历史管理——持久化

​​需求场景​​​​推荐方案​​​​理由​​中小企业级应用&#xff0c;需复杂查询MySQL/PostgreSQL事务支持完善&#xff0c;开发成本低海量数据高并发写入Cassandra水平扩展性强&#xff0c;写入性能高非结构化历史数据快速检索MongoDB灵活存储&#xff0c;内置全文检索本…...