JavaWeb——(13)DBUtils

article/2025/10/3 0:08:09

目录

一、DBUtils介绍

二、DBUtils核心对象

三、DBUtils使用简单案例

四、QueryRunner对象

4.1构造函数

4.2方法

五、ResultSetHandler接口

六、DBUtils控制事务

6.1ThreadLocal

6.2模拟转账业务


一、DBUtils介绍

DBUtils是java编程中的数据库操作实用工具,小巧简单实用,它封装了对JDBC的操作,简化了JDBC操作,主要有以下几点优点

  1. 对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
  2. 对于数据表的写操作,也变得很简单(只需写sql语句)
  3. 可以使用数据源(数据库连接池)、JNDI等技术来优化性能,重用已经构建好的数据库连接对象

二、DBUtils核心对象

DBUtils有三个核心对象,分别为:

  • QueryRunner类
    • QueryRunner中提供对sql语句操作的三个方法
      • query():用于执行select查询操作
      • update():用于执行insert、update、delete等更新操作
      • batch():批处理,一次执行多个操作
  • ResultSetHandler接口
    • 用于定义select操作后,怎样封装结果集
  • DbUtils类
    • 就是一个工具类,定义了关闭资源与事务处理的方法

三、DBUtils使用简单案例

要使用DBUtils工具包,有以下步骤需要执行:

  1. 首先要导入jar包(提取码:2clq)
  2. 创建QueryRunner对象
  3. 使用query方法执行select语句
  4. 使用ResultSetHandler封装结果集
  5. 使用DBUtils类释放资源

首先我们导入DBUtils的包,还有C3P0和mysql连接的包用于连接数据库获取连接池,接着通过C3P0获取数据源(连接池)对象,执行query方法获取结果集,然后将结果集中的值赋给实体对象User,最后输出

    public void testSelect() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());//通过C3P0获取数据源List<User> list = queryRunner.query("SELECT * FROM employee", new BeanListHandler<User>(User.class));//执行查询,获得结果集并将结果复制到实体类User的列表中for (User u:list) {System.out.println(u.toString());}}

输出结果如下:

四、QueryRunner对象

QueryRunner中提供对sql语句操作的三个方法,包括query、update和batch,

4.1构造函数

QueryRunner主要有两种构造函数,

QueryRunner();//无参构造,事务可以手动控制。此对象调用的方法(如:query、update、batrch)参数中要有Connection对象
QueryRunner(DataSource ds);//利用数据源进行构造,事务为自动控制的。一个SQL语句为一个事务。此对象调用的方法(如:query、update、batrch)参数中无需Connection对象

4.2方法

<T> T query(String sql, ResultSetHandler<T> rsh, Object... params);//执行一个带有可变参数(参数个数可以为0)的SQL查询
<T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params);//如果使用无参构造,那么这里就要带上Connection对象int update(String sql, Object... params);//执行一个带有可变参数(参数个数可以为0)的SQL更新,返回更新受影响的行数
int update(Connection conn, String sql, Object... params);//如果使用无参构造,那么这里就要带上Connection对象int[] batch(String sql, Object[][] params);//执行多个INSERT、UPDATE或DELETE语句。params数组第一维指定了批处理语句的个数,第二维指定了每次处理需要参数的个数
int[] batch(Connection conn, String sql, Object[][] params);//如果使用无参构造,那么这里就要带上Connection对象

我们使用上述QueryRunner对象的方法来实现CRUD操作,

//插入操作
public void testInsert() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());//获取QueryRunner对象int line=queryRunner.update("insert into employee(id,name,gender,salary) values(?,?,?)",4,"Rose","FEMALE",6000);if (line>0){System.out.println("数据插入成功!");}
}//修改操作
public void testUpdate() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());//获取QueryRunner对象int line=queryRunner.update("update employee set birthday=?,entry_date=?,job=? where id=?",new Date(),new Date(),"CODER",4);if (line>0){System.out.println("数据更新成功!");}
}//批量处理,只能执行相同的sql语句
public void testBatch() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());//获取QueryRunner对象Object[][] params=new Object[2][];//生成2条语句的参数二维数组for (int i = 0; i < params.length; i++) {params[i]=new Object[]{"User"+(i+1),"MALE",(i+1)*100};//对每个语句参数进行赋值}queryRunner.batch("insert into employee(name,gender,salary) values(?,?,?)",params);
}

 插入结果如图所示:

五、ResultSetHandler接口

ResultSetHandler是用来封装结果集的,但它是一个接口没有具体实现方法,我们用到的主要有以下九种实现:

  1. ArrayHandler:适合取1条记录,把该条记录的每列值封装到数组Object[]中
  2. ArrayListHandler:适合取多条记录,把每条记录的每列值封装到数组Object[]中,把数组封装到列表List中
  3. ColumnListHandler:取某一列的数据,封装到List中
  4. KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值
  5. MapHandler:适合取1条记录,把当前记录的列名和列值放到一个Map中
  6. MapListHandler:适合取多条记录,把每条记录封装到一个Map中,再把Map封装到List中
  7. ScalarHandler:适合取单行单列数据
  8. BeanHandler:适合取1条记录,把当前记录的信息放入对应的实体类中。注意实体类的属性名要和数据表的列名对应。
  9. BeanListHandler:适合取多条记录,把每条记录封装到实体类对象中,再把对象封装到List中

下面我们按顺序逐个测试如何使用这几种类来封装结果集:

//ArrayHandler:适合取1条记录,把该条记录的每列值封装到数组Object[]中
public void test1() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());Object[] arr = queryRunner.query("select * from employee where id=?", new ArrayHandler(), 1);for (Object obj:arr) {System.out.print(obj+"  ");}
}//ArrayListHandler:适合取多条记录,把每条记录的每列值封装到数组Object[]中,把数组封装到列表List中
public void test2() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());List<Object[]> list = queryRunner.query("select * from employee", new ArrayListHandler());for (Object[] objArr:list) {for (Object obj:objArr) {System.out.print(obj+"  ");}System.out.println();}
}//ColumnListHandler:取某一列的数据,封装到List中
public void test3() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());List<Object> list = queryRunner.query("select * from employee", new ColumnListHandler(2));//选取结果集中的第二列数据for (Object obj:list) {System.out.println(obj);}
}//KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值
public void test4() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());Map<Object, Map<String, Object>> map = queryRunner.query("select * from employee", new KeyedHandler(1));//选取结果集中的第一列数据作为键,对应着一个存放用户信息的mapfor (Map.Entry<Object,Map<String,Object>> m: map.entrySet()) {for (Map.Entry<String,Object> mm:m.getValue().entrySet()) {System.out.println(mm.getKey()+":"+mm.getValue());}System.out.println("------------------");}
}//MapHandler:适合取1条记录,把当前记录的列名和列值放到一个Map中
public void test5() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());Map<String, Object> map = queryRunner.query("select * from employee where id=?", new MapHandler(),3);//选取id为3的用户,并将信息存放到map中for (Map.Entry<String,Object> m: map.entrySet()) {System.out.println(m.getKey()+":"+m.getValue());}
}//MapListHandler:适合取多条记录,把每条记录封装到一个Map中,再把Map封装到List中
public void test6() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());List<Map<String, Object>> list = queryRunner.query("select * from employee", new MapListHandler());//将结果集每行信息放在map中,再将map放在list中for (Map<String, Object> map:list) {for (Map.Entry<String, Object> m:map.entrySet()) {System.out.println(m.getKey()+":"+m.getValue());}System.out.println("------------------");}
}//BeanHandler:适合取1条记录,把当前记录的信息放入对应的实体类中
public void test8() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());User user = queryRunner.query("select * from employee where name=?", new BeanHandler<User>(User.class),"Jack");//取单行信息,将结果集中的信息封装到实体类User中System.out.println(user.toString());
}//BeanListHandler:适合取多条记录,把每条记录封装到实体类对象中,再把对象封装到List中
public void test9() throws SQLException{QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());List<User> list = queryRunner.query("select sum(salary) from employee", new BeanListHandler<User>(User.class));//将结果集中的信息封装到实体类User中,并将User放入list中for (User u:list) {System.out.println(u.toString());}
}

六、DBUtils控制事务

6.1ThreadLocal

首先我们介绍一个线程相关的类,ThreadLocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。存储和获取数据就是通过set()和get()方法,调用该类的get方法,只会返回当前线程放入的数据,该数据就是线程的局部变量。

public class TestThreadLocal {public static void main(String[] args) {ThreadLocal threadLocal=new ThreadLocal();threadLocal.set("aaa");MyThread myThread=new MyThread(threadLocal);myThread.start();System.out.println(threadLocal.get());}
}class MyThread extends Thread{private final ThreadLocal threadLocal;public MyThread(ThreadLocal threadLocal){this.threadLocal=threadLocal;}@Overridepublic void run() {System.out.println(threadLocal.get());}
}

可以看到MyThread类创建的线程就获取不到ThreadLocal创建线程的数据,

 ThreadLocal的这种设计就保证了线程的安全,本线程的数据别的线程无法进行访问与修改。

6.2模拟转账业务

当QueryRunner使用无参构造时,就可以手动控制事务,从而在使用ResultSetHandler对象的方法时要传入Connection对象。

我们以转账业务为例,设计一个简单的JavaWeb项目,其设计分层思想如下:

 我们将连接作为对象放在ThreadLocal中,然后通过get方法获取到连接对象,并作为参数传入ResultSetHandler的方法中,从而达到控制事务进而达到事务的原子性,这样如果转账的过程中如果发生异常,就不会出现A的账户钱少了,而B的账户钱没有增加的情况。

首先我们新建一个ThreadLocal的管理类,用于获取数据源的连接以及开启事务关闭事务等操作:

import java.sql.Connection;
import java.sql.SQLException;public class ManageThreadLocal {private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();//得到一个连接public static Connection getConnection(){Connection connection=threadLocal.get();//从当前线程对象中取出连接if (connection==null){//若当前线程无连接connection=C3P0Util.getConnection();//从连接池中取出一个连接threadLocal.set(connection);//将连接设置到线程局部变量中}return connection;}//开始事务public static void startTransaction(){try {Connection connection=getConnection();//东当前线程对象中取出连接connection.setAutoCommit(false);//设置不自动提交事务,手动控制事务} catch (SQLException e) {e.printStackTrace();}}//提交事务public static void commit(){try {Connection connection=getConnection();//东当前线程对象中取出连接connection.commit();//提交事务} catch (SQLException e) {e.printStackTrace();}}//事务回滚public static void rollback(){try {Connection connection=getConnection();//东当前线程对象中取出连接connection.rollback();//事务回滚} catch (SQLException e) {e.printStackTrace();}}//断开连接public static void close(){try {Connection connection=getConnection();//东当前线程对象中取出连接connection.close();//断开连接,放回连接池中threadLocal.remove();//将当前线程对象中存放的连接移除} catch (SQLException e) {e.printStackTrace();}}}

然后是数据访问层

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;public class AccountDaoImpl implements AccountDao {@Overridepublic void updateAccount(String payer, String payee, double money) throws SQLException {QueryRunner queryRunner=new QueryRunner(C3P0Util.getDataSource());//创建QueryRunner对象queryRunner.update("update account set money=money-? where name=?",money,payer);//转出queryRunner.update("update account set money=money+? where name=?",money,payee);//转入}@Overridepublic void updateAccount(Account account) throws SQLException{QueryRunner queryRunner=new QueryRunner();//无参创建QueryRunner对象queryRunner.update(ManageThreadLocal.getConnection(),"update account set money=? where name=?",account.getMoney(),account.getName());//根据账户信息修改}@Overridepublic Account findAccountByName(String name) throws SQLException {QueryRunner queryRunner=new QueryRunner();//无参创建QueryRunner对象Account account = queryRunner.query(ManageThreadLocal.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class), name);//根据用户名找到用户,并将结果集放在Account对象中return account;}
}interface AccountDao {/*** 转账* @param payer 付款方账户名* @param payee 收款方账户名* @param money 转账金额*/@Deprecatedpublic void updateAccount(String payer,String payee,double money)throws SQLException;/*** 根据账户信息修改金额* @param account 修改的账户*/public void updateAccount(Account account)throws SQLException;/*** 根据用户名查找账户信息* @param name 用户名* @return 查找的账户* @throws Exception*/public Account findAccountByName(String name)throws SQLException;
}

然后是业务处理层

public class AccountServiceImpl implements AccountService {@Overridepublic void transfer(String payer, String payee, double money){AccountDao accountDao=new AccountDaoImpl();try{//开始事务ManageThreadLocal.startTransaction();//获取付款方和收款方账户Account payerAcc = accountDao.findAccountByName(payer);Account payeeAcc = accountDao.findAccountByName(payee);//修改账户金额payerAcc.setMoney(payerAcc.getMoney()-money);payeeAcc.setMoney(payeeAcc.getMoney()+money);//将账户写入accountDao.updateAccount(payerAcc);accountDao.updateAccount(payeeAcc);//提交事务ManageThreadLocal.commit();}catch (Exception e){ManageThreadLocal.rollback();//回滚事务}finally {ManageThreadLocal.close();//关闭连接}}
}interface AccountService {public void transfer(String payer, String payee, double money);
}

接着我们进行转账测试:

public class TestTransfer {public static void main(String[] args) throws Exception{AccountService accountService=new AccountServiceImpl();accountService.transfer("aaa","bbb",100);}
}

后台数据库数据变更前后:


http://chatgpt.dhexx.cn/article/GjfUiCbZ.shtml

相关文章

DBUtils工具类的使用方法详解

DBUtils使用方法详解 目录 DBUtils使用方法详解一、前言二、JDBC介绍1.基本概念2.JDBC访问数据库的流程 三、DBUtils介绍1.基本概念2.配置文件3.创建JDBCUtils类4.实现对数据表的增删改查 四、对以上代码的说明五、总结 一、前言 本文是关于DBUtils使用方法的介绍&#xff0c;…

如何用计算机解kkt条件,【直观详解】拉格朗日乘法和KKT条件

【阅读时间】8min - 10mun 【内容简介】直观的解读了什么是拉格朗日乘子法,以及如何求解拉格朗日方程,并且给出几个直观的例子,针对不等式约束解读了KKT条件的必要条件和充分条件 What & Why 拉格朗日乘法(Lagrange multiplier)是一种在最优化的问题中寻找多元函数在其变…

SVM中的KKT条件和拉格朗日对偶

首先&#xff0c;我们要理解KKT条件是用来干嘛的&#xff1f; KKT条件用来判断一个解是否属于一个非线性优化问题。 求最优解&#xff1a; 约束条件分为 1、等式约束 2、不等式约束 对于等式约束的优化问题&#xff0c;可以直接应用拉格朗日乘子法去求取最优值&#xff1b; …

KKT条件详解

KKT条件详解 主要参考这篇文章和这个知乎回答。 KKT最优化条件是Karush[1939]&#xff0c;以及Kuhn和Tucker[1951]先后独立发表出來的。这组最优化条件在Kuhn和Tucker发表之后才逐渐受到重视&#xff0c;因此许多情况下只记载成库恩塔克条件&#xff08;Kuhn-Tucker conditions…

KKT条件总结

最近学习的时候用到了最优化理论&#xff0c;但是我没有多少这方面的理论基础。于是翻了很多大神的博客把容易理解的内容记载到这篇博客中。因此这是篇汇总博客&#xff0c;不算是全部原创&#xff0c;但是基础理论&#xff0c;应该也都差不多吧。因才疏学浅&#xff0c;有纰漏…

KKT条件

无约束优化 首先给出一些基本概念定义&#xff1a; 凸集&#xff1a; 欧式空间中&#xff0c;集合中任意两点的连线都在集合中&#xff0c;我们就说这个集合是凸集&#xff1b;凸函数&#xff1a; 对于任意属于[0,1]的a和任意属于凸集的两点x, y&#xff0c;有 f ( a x ( 1 …

KKT 直观理解

KKT最优化条件是Karush[1939]&#xff0c;以及Kuhn和Tucker[1951]先后独立发表出來的。这组最优化条件在Kuhn和Tucker发表之后才逐渐受到重视&#xff0c;因此许多情况下只记载成库恩塔克条件&#xff08;Kuhn-Tucker conditions) 库恩塔克条件(Kuhn-Tucker conditions)是非线性…

判断kkt条件的例题_kkt条件例题(kkt条件例题求解)

kkt条件例题(kkt条件例题求解) 2020-05-08 10:54:39 共10个回答 要看目标函数的斜率,不能单凭横坐标或纵坐标确定追问能举例说明吗回答一般线性规划的图像解法是通过平移一条直线,观察与可行域的焦点来求极值的这个还是线性规划里比较基础的问题.建议你找一本线性规划的书或者是…

kkt条件的理解_直观理解KKT条件

KKT最优化条件是Karush[1939],以及Kuhn和Tucker[1951]先后独立发表出來的。这组最优化条件在Kuhn和Tucker发表之后才逐渐受到重视,因此许多情况下只记载成库恩塔克条件(Kuhn-Tucker conditions) 库恩塔克条件(Kuhn-Tucker conditions)是非线性规划领域里最重要的理论成果之一…

对偶专题——KKT条件

[对偶专题——Duality and Dual problem (一) https://blog.csdn.net/jmh1996/article/details/85030323] 对于一般的带约束的优化问题&#xff1a; 介绍了如何通过构造原优化目标的一个下界函数 L ( x , λ , u ) L(x,\lambda,u) L(x,λ,u)&#xff0c;这一般通过添加一些线性…

java telnet端口_Java 实现 telnet命令 验证主机端口的连通性

Java 实现 telnet命令 验证主机端口的连通性 1、Telnet 命令 Telnet协议是TCP/IP协议族中的一员,是Internet远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在…

解决阿里云服务器telnet端口不成功的问题

问题还原 阿里服务器部署了一个API站点&#xff0c;端口为8079&#xff0c;本地电脑怎么telnet到这个端口都不成功 排查 防火墙 要么把防火墙全部关闭&#xff0c;或者配置入站出站规则&#xff0c;这里我是直接关闭服务器的防火墙 查看端口是否正常监听 确保系统内部必须…

能ping通,但是telnet端口连接失败

背景&#xff1a;在windows上创建了客户端&#xff0c;使用腾讯云的轻量服务器创建的Linux服务器。 问题&#xff1a;服务器操作系统防火墙已关闭&#xff0c;使用ping命令没问题&#xff0c;但是使用telnet测试端口的时候&#xff0c;连接失败。 解决&#xff1a;一开始添加了…

telnet端口不通怎么解决(单边不通的方法建议)

telnet端口不通是大家在检测端口的时候可能会遇到的问题之一&#xff0c;遇到这种状况一般要如何解决呢&#xff1f;这里为各位带来分享&#xff0c;看一下telnet端口不通的解决方式&#xff0c;看一下如何处理吧。 telnet端口不通怎么解决 1.开放供应商服务器端口 总是出现…

telnet服务器端口

telnet服务器端口 1.Windows 机器使用telnet命令1.2开启telnet功能1.2 使用telnet 1.Windows 机器使用telnet命令 1.2开启telnet功能 可以通过下面步骤进入程序和功能 然后找到启用或关闭Windows功能 然后勾选上Telnet客户端&#xff0c;代表功能开启。 1.2 使用telnet …

telnet命令及测试网络端口的几种方法

1、常见的用法&#xff1a; telnet IP port 如&#xff1a;telnet 192.168.1.10 80 端口&#xff0c;如果端口没有开启监听则会显示连接失败。 若端口有开启监听&#xff0c;telnet端口是通的会显示一个白色的光标并闪烁&#xff0c;如下图&#xff1a; 2、Linux下其余几种测…

Telnet端口

出现“Telnet不是内部命令”的相关提示&#xff0c;说明Telnet客户端没有开启 一、开启Telnet客户端 1、打开控制面板&#xff0c;点击“程序” 2、选择“启用或关闭Widows功能” 3、我们可以看到“Telnet Client”没有勾选&#xff0c;即没有开启 4、勾选“Telnet Client”…

telnet用法 测试端口号

Telnet是进行远程登录的标准协议和主要方式它为用户提供了在本地计算机上完成远程主机工作的能力。可以用telnet命令来测试端口号是否正常打开还是关闭状态。 方法/步骤 点击计算机的开始菜单--》运行 &#xff0c;输入CMD命令&#xff0c;然后确定。打开cmd命令行。 输入tel…