JDBC基础自查手册
本文最后更新于:2020年12月30日 下午
一、概述
远古技术了,没人带就一点一点来吧
二、入门
创建数据库和表
create database web_test3; use web_test3; create table user( id int primary key auto_increment, username varchar(20), password varchar(20), nickname varchar(20), age int ); insert into user values (null,'aaa','123','xiaoli',34); insert into user values (null,'bbb','123','dawang',32); insert into user values (null,'ccc','123','xiaoming',28); insert into user values (null,'ddd','123','dahuang',21);
创建项目,引入mysql-connector-java的jar包,如将
mysql-connector-java-5.0.8-bin.jar
放在lib文件夹下开发JDBC
- 加载驱动
- 获得连接
- 基本操作
- 释放资源
import org.junit.Test; /** * JDBC的入门程序 * @author jt * */ public class JDBCDemo1 { @Test public void demo1() throws Exception{ // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获得JDBC连接(url,username,pwd) Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "abc"); // 3.基本操作:执行SQL // 3.1获得执行SQL语句的对象 Statement statement = conn.createStatement(); // 3.2编写SQL语句: String sql = "select * from user"; // 3.3执行SQL: ResultSet rs = statement.executeQuery(sql); // 3.4遍历结果集: while(rs.next()){ System.out.print(rs.getInt("id")+" "); System.out.print(rs.getString("username")+" "); System.out.print(rs.getString("password")+" "); System.out.print(rs.getString("nickname")+" "); System.out.print(rs.getInt("age")); System.out.println(); } // 4.释放资源 rs.close(); statement.close(); conn.close(); } }
三、常用API
1)DriverManager 驱动管理类
注册驱动
DriverManager.registerDriver(new Driver());
一般不会用,更推荐Class.forName(“com.mysql.jdbc.Driver”);
获得连接:
DriverManager.getConnection(String url, String user, String password)
- “jdbc:mysql://localhost:3306/web_test3”, “root”, “abc”
- jdbc:连接数据库的协议,mysql:是jdbc的子协议
- localhost:连接的MySQL数据库服务器的主机地址。(连接是本机就可以写成localhost),如果连接不是本机的,就需要写上连接主机的IP地址。
- 3306:MySQL数据库服务器的端口号,web_test3:数据库名称
url如果连接的是本机的路径,可以简化为:
jdbc:mysql:///web_test3
2)Connection 与数据库连接对象
- 创建执行SQL语句的对象
Connection.createStatement()
:创建一个Statement对象,来执行SQLConnection.prepareCall(String sql)
:创建一个CallableStatement对象,来执行数据库中存储过程Connection.PreparedStatement
:创建一个PreparedStatement对象,来讲参数化的SQL语句发送到数据库。对SQL进行预处理,解决SQL注入漏洞
- 管理事务
setAutoCommit
:设置自动提交commit()
:提交rollback()
:回滚,取消在当前事务中进行的所有更改
3)Statement
- 执行SQL语句:
statement.execute(String sql)
:执行增删改查等SQL语句statement.executeQuery(String sql)
:执行查询(select语句)statement.executeUpate(String sql)
:执行修改,添加,删除的SQL语句
- 执行批处理:
statement.addBatch(String sql)
:将给定的SQL命令添加到此statement对象的当前命令列表中statement.clearBatch()
:清空此statement对象的当前SQL命令列表statement.executeBatch()
:将一批SQL命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数 组成的数组
4)ResultSet 结果集
通过select语句得到的查询结果
- 结果集遍历:
ResultSet.next()
- 结果集获取:
ResultSet.getXXX(int columnIndex)
或者ResultSet.getXXX(String columnName)
四、资源释放
JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是Connection,Statement,ResultSet。
这几个对象中尤其是Connection对象是非常稀有的。这个对象一定要做到尽量晚创建,尽早释放掉。
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(statement !=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
statement = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
五、JDBC的CRUD操作
Create增加、Retrieve查询、Update更新、Delete删除
通过Statement实现
@Test /** * 保存操作的代码实现 */ public void demo1(){ Connection conn = null; Statement stmt = null; try{ // 注册驱动: Class.forName("com.mysql.jdbc.Driver"); // 获得连接: conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc"); // 执行操作: // 创建执行SQL语句对象: stmt = conn.createStatement(); // 编写SQL语句: String sql = "insert into user values (null,'eee','123','阿黄',21)"; // 执行SQL语句: int num = stmt.executeUpdate(sql); if(num > 0){ System.out.println("保存用户成功!!!"); } }catch(Exception e){ e.printStackTrace(); }finally{ // 资源释放: if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } @Test /** * 更新操作(update)的代码实现 */ public void demo2(){ Connection conn = null; Statement stmt = null; try{ // 注册驱动: Class.forName("com.mysql.jdbc.Driver"); // 获得连接 conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc"); // 执行操作: // 创建执行SQL语句的对象: stmt = conn.createStatement(); // 编写SQL语句: String sql = "update user set password='abc',nickname='旺财' where id = 5"; // 执行SQL语句: int num = stmt.executeUpdate(sql); if(num > 0){ System.out.println("修改用户成功!!!"); } }catch(Exception e){ e.printStackTrace(); }finally{ // 资源释放: if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } @Test /** * 查询多条记录 */ public void demo4(){ Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ // 注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 获得连接 conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc"); // 执行操作 // 创建执行SQL语句的对象: stmt = conn.createStatement(); // 编写SQL: String sql = "select * from user"; // 执行SQL: rs = stmt.executeQuery(sql); // 遍历结果集: while(rs.next()){ System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")); } }catch(Exception e){ e.printStackTrace(); }finally{ // 资源释放: if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } @Test /** * 查询一条记录 */ public void demo5(){ Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ // 注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 获得连接 conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc"); // 执行SQL // 创建执行SQL语句对象: stmt = conn.createStatement(); // 编写SQL: String sql = "select * from user where id = 4"; rs = stmt.executeQuery(sql); // 判断就可以: if(rs.next()){ System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")); } }catch(Exception e){ e.printStackTrace(); }finally{ // 资源释放: if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } @Test /** * 删除操作的代码实现 */ public void demo3(){ Connection conn = null; Statement stmt = null; try{ // 注册驱动: Class.forName("com.mysql.jdbc.Driver"); // 获得连接: conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc"); // 创建执行SQL语句对象: stmt = conn.createStatement(); // 编写SQL: String sql = "delete from user where id = 5"; // 执行SQL: int num = stmt.executeUpdate(sql); if(num > 0){ System.out.println("删除用户成功!!!"); } }catch(Exception e){ e.printStackTrace(); }finally{ // 资源释放: if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } }
通过PreparedStatement实现
@Test /** * 保存操作 */ public void demo1(){ Connection conn = null; PreparedStatement pstmt = null; try{ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL语句: String sql = "insert into user values (null,?,?,?,?)"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数: pstmt.setString(1, "eee"); pstmt.setString(2, "abc"); pstmt.setString(3, "旺财"); pstmt.setInt(4, 32); // 执行SQL int num = pstmt.executeUpdate(); if(num > 0){ System.out.println("保存成功!"); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(pstmt, conn); } } @Test /** * 修改操作 */ public void demo2(){ Connection conn = null; PreparedStatement pstmt = null; try{ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL语句: String sql = "update user set username = ?,password =?,nickname=?,age = ? where id = ?"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数: pstmt.setString(1, "abc"); pstmt.setString(2, "1234"); pstmt.setString(3, "旺旺"); pstmt.setInt(4, 23); pstmt.setInt(5, 6); // 执行SQL: int num = pstmt.executeUpdate(); if(num > 0){ System.out.println("修改成功!"); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(pstmt, conn); } } @Test /** * 删除操作 */ public void demo3(){ Connection conn = null; PreparedStatement pstmt = null; try{ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL语句: String sql = "delete from user where id = ?"; // 预编译SQL pstmt = conn.prepareStatement(sql); // 设置参数: pstmt.setInt(1, 4); // 执行SQL: int num = pstmt.executeUpdate(); if(num > 0){ System.out.println("删除成功!"); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(pstmt, conn); } } @Test /** * 查询操作 */ public void demo4(){ Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try{ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL: String sql = "select * from user"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数: // 执行SQL: rs = pstmt.executeQuery(); // 遍历结果集: while(rs.next()){ System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("nickname")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, pstmt, conn); } }
六、工具类的抽取
因为传统JDBC的开发,注册驱动,获得连接,释放资源这些代码都是重复编写的。所以可以将重复的代码提取到一个类中来完成。
/**
* JDBC的工具类
* @author jt
*
*/
public class JDBCUtils {
private static final String driverClassName;
private static final String url;
private static final String username;
private static final String password;
static{
driverClassName="com.mysql.jdbc.Driver";
url="jdbc:mysql:///web_test3";
username="root";
password="abc";
}
/**
* 1.注册驱动
*/
public static void loadDriver(){
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 2.获得连接
*/
public static Connection getConnection(){
Connection conn = null;
try{
// 将驱动一并注册:
loadDriver();
// 获得连接
conn = DriverManager.getConnection(url,username, password);
}catch(Exception e){
e.printStackTrace();
}
return conn;
}
/**
* 3.释放资源
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
// 资源释放:
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
测试下工具类,具体如下:
@Test
/**
* 查询操作:使用工具类
*/
public void demo1(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 创建执行SQL语句的对象:
stmt = conn.createStatement();
// 编写SQL:
String sql = "select * from user";
// 执行查询:
rs = stmt.executeQuery(sql);
// 遍历结果集:
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放资源:
JDBCUtils.release(rs, stmt, conn);
}
}
七、配置文件
属性文件db.properties
,其由key=value
组成
定义配置文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://web_test3 username=root password=abc
在工具类中解析属性文件
static{ //获取属性文件中的内容 Properties properties = new Properties(); try{ properties.load(new FileInputStream("src/db.properties")); } catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } driverClassname=properties. getproperty("driverClassName"); url=properties.getproperty("url"); username=properties.getproperty("username") password=properties.getproperty("password") }
八、SQL注入漏洞
只知道用户名,无需密码即可完成登录。
原因分析:当采取用户名aaa’ or ‘1=1
时,select * from user where username = ‘“+username”’ and password = ‘“+password”’
变成了select * from user where username = ‘aaa’ or ‘1=1’and password = ‘“+password”’
,有一个or产生了,只要用户名存在即可通过。
解决方法:使用PreparedStatement对象
替换掉Statement对象
,将SQL预先进行编译,使用?
作为占位符,之后再传入变量。
public class UserDao {
public boolean login(String username,String password){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// 定义一个变量:
boolean flag = false;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 编写SQL语句:
String sql = "select * from user where username = ? and password = ?";
// 预编译SQL
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, username);
pstmt.setString(2, password);
// 执行SQL语句:
rs = pstmt.executeQuery();
if(rs.next()){
// 说明根据用户名和密码可以查询到这条记录
flag = true;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
return flag;
}
}
/**
* SQL 注入漏洞
*/
public class JDBCDemo4{
public void demo1(){
UserDao userDao = new UserDao();
boolean flag = userDao.login("aaa' or '1=1","qweerttyu");
boolean flag = userDao.login("aaa' -- ","qweerttyu");
if (flag){
System.out.println("登陆成功");
} else{
System.out.println("登陆失败");
}
}
}
九、批处理
批量执行SQL语句
@Test
/**
* 批处理基本操作
*/
public void demo1(){
Connection conn = null;
Statement stmt = null;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 创建执行批处理对象:
stmt = conn.createStatement();
// 编写一批SQL语句:
String sql1 = "create database test1";
String sql2 = "use test1";
String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql4 = "insert into user values (null,'aaa')";
String sql5 = "insert into user values (null,'bbb')";
String sql6 = "insert into user values (null,'ccc')";
String sql7 = "update user set name = 'mmm' where id = 2";
String sql8 = "delete from user where id = 1";
// 添加到批处理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
stmt.addBatch(sql7);
stmt.addBatch(sql8);
// 执行批处理:
stmt.executeBatch();
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(stmt, conn);
}
}
@Test
/**
* 批量插入记录:
* * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可。
*/
public void demo2(){
// 记录开始时间:
long begin = System.currentTimeMillis();
Connection conn = null;
PreparedStatement pstmt = null;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 编写SQL语句:
String sql = "insert into user values (null,?)";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
for(int i=1;i<=10000;i++){
pstmt.setString(1, "name"+i);
// 添加到批处理
pstmt.addBatch();
// 注意问题:
// 一段时间执行一次批处理,避免内存溢出
if(i % 1000 == 0){
// 执行批处理:
pstmt.executeBatch();
// 清空批处理:
pstmt.clearBatch();
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
long end = System.currentTimeMillis();
System.out.println((end-begin));
}
十、事务
事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败。
环境准备:
创建数据库和表
create database web_test4; use web_test4; create table account( id int primary key auto_increment, name varchar(20), money double ); insert into account values (null,'aaa',10000); insert into account values (null,'bbb',10000); insert into account values (null,'ccc',10000);
转账案例代码
@Test /** * 完成转账的案例 */ public void demo1(){ Connection conn = null; PreparedStatement pstmt = null; try{ /** * 完成转账代码: * * 扣除某个账号的钱 * * 给另外一个账号加钱 */ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL语句: String sql = "update account set money = money + ? where name = ?"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数: // 用aaa账号给bbb账号转1000元 pstmt.setDouble(1, -1000); pstmt.setString(2, "aaa"); // 执行SQL:扣除aaa账号1000元 pstmt.executeUpdate(); // int i = 1 / 0; // 在转账中没有添加事务的管理,出现aaa账号的钱被转丢了,但是bbb账号的钱没有任何变化。需要给转账的功能添加事务的管理。 // 给bbb账号加1000 pstmt.setDouble(1, 1000); pstmt.setString(2, "bbb"); pstmt.executeUpdate(); }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(pstmt, conn); } }
事务管理API
setAutoCommit
:此连接自动提交事务commit
:提交事务rollback
:回滚
@Test /** * 完成转账的案例 */ public void demo1(){ Connection conn = null; PreparedStatement pstmt = null; try{ /** * 完成转账代码: * * 扣除某个账号的钱 * * 给另外一个账号加钱 */ // 获得连接: conn = JDBCUtils.getConnection(); // 开启事务 conn.setAutoCommit(false); // 编写SQL语句: String sql = "update account set money = money + ? where name = ?"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数: // 用aaa账号给bbb账号转1000元 pstmt.setDouble(1, -1000); pstmt.setString(2, "aaa"); // 执行SQL:扣除aaa账号1000元 pstmt.executeUpdate(); int i = 1 / 0; // 给bbb账号加1000 pstmt.setDouble(1, 1000); pstmt.setString(2, "bbb"); pstmt.executeUpdate(); // 提交事务: conn.commit(); }catch(Exception e){ // 回滚事务,避免钱中途飞了 try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally{ JDBCUtils.release(pstmt, conn); } }
十一、连接池
1)概述
连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。
提升性能:连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。
2)Druid 开源连接池
Druid是阿里旗下的开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。
@Test
/**
* Druid的使用:
* * 手动设置参数的方式
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 使用连接池:
DruidDataSource dataSource = new DruidDataSource();
// 手动设置数据库连接的参数:
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///web_test4");
dataSource.setUsername("root");
dataSource.setPassword("abc");
// 获得连接:
// conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from account";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 执行SQL:
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
也可以采用配置文件设置参数,从而使用Druid连接池
@Test
/**
* Druid的使用:
* * 配置方式设置参数
* Druid配置方式可以使用属性文件配置的。
* * 文件名称没有规定但是属性文件中的key要一定的。
*/
public void demo2(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 使用连接池:
// 从属性文件中获取:
Properties properties = new Properties();
properties.load(new FileInputStream("src/druid.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获得连接:
// conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from account";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 执行SQL:
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
3)C3P0 连接池
C3P0也是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准拓展。目前使用它的开源项目有Hibernate和Spring等。
手动设置参数
@Test /** * 手动设置参数的方式: */ public void demo1(){ Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try{ // 获得连接:从连接池中获取: // 创建连接池: ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 设置连接参数: dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql:///web_test4"); dataSource.setUser("root"); dataSource.setPassword("abc"); // 从连接池中获得连接: conn = dataSource.getConnection(); // 编写SQL: String sql = "select * from account"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 执行SQL: rs = pstmt.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, pstmt, conn); } }
采用配置文件的方式:
配置连接池
使用连接池
@Test /** * 采用配置文件的方式: */ public void demo2(){ Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try{ // 获得连接:从连接池中获取: // 创建连接池:创建连接池默认去类路径下查找c3p0-config.xml ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 可以指定名称,找不到时会使用默认的 // 从连接池中获得连接: conn = dataSource.getConnection(); // 编写SQL: String sql = "select * from account"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 执行SQL: rs = pstmt.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, pstmt, conn); } }
4)改写工具类
连接池对象应该是一个应用只创建一次就可以的,不需要每次使用均创建一个新的连接池
十二、DBUtils
DBUtils是一个对JDBC进行简单封装的开源工具类库,将重复的代码(获得连接、预编译SQL、释放资源等)抽取出来放到工具类中,简化JDBC的编程。
1)API概述
核心运行类
QueryRunner
:有参、无参构造均可
常用方法有update、query、batch等
// 一般情况下执行Crud操作 // 1.构造 QueryRunner(DataSource ds); // 2.方法 int update(String sql,Object… args); T query(String sql,ResultSetHandler rsh,Object… args); // 执行SQL语句,返回结果对象 // 当有事务管理,且要执行CRUD时, // 1.构造(不传入连接池): QueryRunner(); // 2.方法(参数包含连接): int update(Connection conn,String sql,Object… args); T query(Connection conn,String sql,ResultSetHandler rsh,Object… args);
DbUtils类:
commitAndCloseQuietly(connection conn)
:提交连接然后关闭它rollbackAndCloseQuietly(connection conn)
:回滚连接,然后关闭
2)增删查改示例
@Test
/**
* 添加、删除、修改操作,具体的操作由对应的sql语句区分,其他都一样
*/
public void demo1() throws SQLException{
// 创建核心类:QueryRunner:
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
// 调用update方法
queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
queryRunner.update("delete from account where id = ?", 3);
queryRunner.update("update account set name=?,money=? where id =?", "eee",20000,4);
}
// 查询
// 1.查询一条记录
@Test
public class demo1 {
// 创建核心类
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
// 执行查询
Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<account>(){
@Override
public Account handle(ResultSet rs){
Account account = new Account();
if(rs.next()){
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
}
return account;
}
},1);
System.out.println(account);
}
// 2.查询多条记录
@Test
public class demo2 {
// 创建核心类
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
// 执行查询
Account account = queryRunner.query("select * from account", new ResultSetHandler<list<account>>(){
@Override
public List<Account> handle(ResultSet rs){
// 创建集合来封装数据
List<Account> list = new ArrayList<Account>();
while(rs.next()){
// 封装数据
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
list.add(account);
}
return list;
}
});
// 显示
for(Account account : list){
System.out.println(account);
}
}
3)ResultSetHandler实现类
ArrayHandler
:将一条记录封装到一个Object数组中ArrayListHandler
:将多条记录封装到一个装有Object数组的集合中BeanHandler
:将一条记录封装到一个JavaBean中BeanListHandler
:将多条记录封装到一个装有JavaBean的List集合中MapHandler
:将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值MapListHandler
:将多条记录封装到一个装有Map的List集合中。ColumnListHandler
:将数据中的某列封装到List集合中ScalarHandler
:封装单个值KeyedHandler
:将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。package com.itheima.dbutils.demo3; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ColumnListHandler; import org.apache.commons.dbutils.handlers.KeyedHandler; import org.apache.commons.dbutils.handlers.MapHandler; import org.apache.commons.dbutils.handlers.MapListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import org.junit.Test; import com.itheima.dbutils.domain.Account; import com.itheima.jdbc.utils.JDBCUtils2; /** * ResultSetHandler的实现类 */ public class DBUtilsDemo3 { @Test /** * ArrayHandler:将一条记录封装到一个Object数组中 */ public void demo1() throws SQLException { QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); Object[] objs = queryRunner.query("select * from account where id = ?", new ArrayHandler(), 1); System.out.println(Arrays.toString(objs)); } @Test /** * ArrayListHandler:将多条记录封装到一个装有Object数组的List集合中 * * 一条记录封装到Objecct[]数组中,多条记录就是多个Object[],那么多个Object数组就将其装入List集合中即可。 */ public void demo2() throws SQLException { QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); List<Object[]> list = queryRunner.query("select * from account", new ArrayListHandler()); for (Object[] objects : list) { System.out.println(Arrays.toString(objects)); } } @Test /** * BeanHandler:将一条记录封装到一个JavaBean中 */ public void demo3() throws SQLException { QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); Account account = queryRunner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), 2); System.out.println(account); } @Test /** * BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中。 * *一条记录就是一个Java的对象(JavaBean),如果多条记录(多个Java的对象),将多个Java对象装到一个List集合中。 */ public void demo4() throws SQLException { QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); List<Account> list = queryRunner.query("select * from account ", new BeanListHandler<Account>(Account.class)); for (Account account : list) { System.out.println(account); } } @Test /** * MapHandler:将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值。 */ public void demo5() throws SQLException{ QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); Map<String,Object> map = queryRunner.query("select * from account where id = ?", new MapHandler(), 4); System.out.println(map); } @Test /** * MapListHandler:将多条记录封装到一个装有Map的List集合中。 */ public void demo6() throws SQLException{ QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); List<Map<String,Object>> list = queryRunner.query("select * from account", new MapListHandler()); for (Map<String, Object> map : list) { System.out.println(map); } } @Test /** * ColumnListHandler:将某列的值封装到List集合中 */ public void demo7() throws SQLException{ QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); List<Object> list = queryRunner.query("select name,money from account", new ColumnListHandler("name")); for (Object object : list) { System.out.println(object); } } @Test /** * ScalarHandler:单值封装 */ public void demo8() throws SQLException{ QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); Object obj = queryRunner.query("select count(*) from account", new ScalarHandler()); System.out.println(obj); } @Test /** * KeyedHandler:将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。 */ public void demo9() throws SQLException{ QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource()); Map<Object,Map<String,Object>> map = queryRunner.query("select * from account", new KeyedHandler("id")); for (Object key : map.keySet()) { System.out.println(key+" "+map.get(key)); } } }
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!