栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JDBC学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDBC学习

JDBC (Java Database Connection) 是通过JAVA访问数据库

这里对MySQL基本的增删改查进行简单复习。

MySQL 访问数据库步骤

1) 注册和加载驱动(可以省略)
2) 获取连接
3) Connection 获取 Statement 对象
4) 使用 Statement 对象执行 SQL 语句
5) 返回结果集
6) 释放资源

为项目导入mysql-jdbc的jar包

访问MySQL数据库需要用到第三方的类,这些第三方的类,都被压缩在一个叫做Jar的文件里。
为了代码能够使用第三方的类,需要为项目导入mysql的专用Jar包。 通常都会把项目用到的jar包统一放在项目的lib目录下,在本例就会放在
E:projectj2selib 这个位置 然后在eclipse中导入这个jar包 导包步骤:
右键project->property->java build path->libaries->add external jars

初始化驱动

通过Class.forName(“com.mysql.jdbc.Driver”);
初始化驱动类com.mysql.jdbc.Driver
就在 mysql-connector-java-5.0.8-bin.jar中
如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException

Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。

package jdbc;
public class TestJDBC {
    public static void main(String[] args) {      
        //初始化驱动
        try {
            //驱动类com.mysql.jdbc.Driver
            //就在 mysql-connector-java-5.0.8-bin.jar中
            //如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException         Class.forName("com.mysql.jdbc.Driver");          
            System.out.println("数据库驱动加载成功 !");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }     
    }
}
建立与数据库的连接

建立与数据库的Connection连接
这里需要提供:
数据库所处于的ip:127.0.0.1 (本机)
数据库的端口号: 3306 (mysql专用端口号)
数据库名称 how2java
编码方式 UTF-8
账号 root
密码 admin

这一步要成功执行,必须建立在mysql中有数据库how2java的基础上,如果没有,点击创建数据库进行数据库的创建。

package jdbc;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
public class TestJDBC {
    public static void main(String[] args) {
 
        try {
            Class.forName("com.mysql.jdbc.Driver");
 
            // 建立与数据库的Connection连接
            // 这里需要提供:
            // 数据库所处于的ip:127.0.0.1 (本机)
            // 数据库的端口号: 3306 (mysql专用端口号)
            // 数据库名称 how2java
            // 编码方式 UTF-8
            // 账号 root
            // 密码 admin
 
            Connection c = DriverManager
                    .getConnection(
                            "jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
                            "root", "admin");
 
            System.out.println("连接成功,获取连接对象: " + c);
 
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

创建Statement

Statement是用于执行SQL语句的,比如增加,删除。
代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象

执行SQL语句

s.execute执行sql语句,执行成功后,进行查看,明确插入成功。执行SQL语句之前要确保数据库A中有表B的存在,如果没有,需要事先创建表B

package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
    public static void main(String[] args) {
        try {    Class.forName("com.mysql.jdbc.Driver");
            Connection c = DriverManager
                    .getConnection(
                            "jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
                            "root", "admin");
            Statement s = c.createStatement();
 
            // 准备sql语句
            // 注意: 字符串要用单引号'
            String sql = "insert into hero values(null,"+"'提莫'"+","+313.0f+","+50+")"; 
            s.execute(sql);
 
            System.out.println("执行插入语句成功");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
关闭连接

数据库的连接是有限资源,相关操作结束后,养成关闭数据库的好习惯
先关闭Statement
后关闭Connection
释放资源
1) 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
2) 释放原则:先开的后关,后开的先关。ResultSet 、Statement 、 Connection
3) 放在哪个代码块中:finally 块

package jdbc;

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

public class TestJDBC {
	public static void main(String[] args) {

		Connection c = null;
		Statement s = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");

			c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root",
					"admin");

			s = c.createStatement();

			String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";

			s.execute(sql);

		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			// 数据库的连接时有限资源,相关操作结束后,养成关闭数据库的好习惯
			// 先关闭Statement
			if (s != null)
				try {
					s.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			// 后关闭Connection
			if (c != null)
				try {
					c.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

		}

	}
}

使用try-with-resource的方式自动关闭连接

如果觉得上一步的关闭连接的方式很麻烦,可以参考关闭流
的方式,使用try-with-resource的方式自动关闭连接,因为Connection和Statement都实现了AutoCloseable接口

package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
    public static void main(String[] args) {
        try {
Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try (
            Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
                "root", "admin");
            Statement s = c.createStatement();              
        )
        {
            String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
            s.execute(sql);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

循环插入100条数据
package com.JDBC;

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

public class TestJDBC1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
try {
	Class.forName("com.mysql.jdbc.Driver");
	System.out.println("数据库驱动加载成功 !");
}catch(ClassNotFoundException e) {
	e.printStackTrace();
}
String url="jdbc:mysql://127.0.0.1:3306/data1?characterEncoding=UTF-8";
try (Connection c= DriverManager.getConnection(url, "root", "huabao970716");
		Statement s=c.createStatement();
		){
	int count=8;
 // 注意: 字符串要引用单引号'
	
//注意"    '英雄    "+count+"  ',   ",最左和最右边分别有一个单引号,凡是字符串的地方必须要有单引号
	for(int i=0;i<100;i++) {
		String sql="insert into student values("+count+","
	+"'英雄"+count+
				"',"+count+","+"'男"+"',"+count+")";
		s.execute(sql);
		count++;
	}
	System.out.println("执行插入语句成功");
}catch(SQLException e) {
	e.printStackTrace();
}
	}

}

增、删、改

CRUD是最常见的数据库操作,即增删改查
C 增加(Create)
R 读取查询(Retrieve)
U 更新(Update)
D 删除(Delete)
在JDBC中增加,删除,修改的操作都很类似,只是传递不同的SQL语句就行了。查询因为要返回数据,所以和上面的不一样

增加

就是上述代码这里不再赘述

删除

其实就是数据库的删除操作。

 String sql = "delete from hero where id = 5";
修改
  String sql = "update hero set name = 'name 5' where id = 3";
执行查询SQL语句

执行查询SQL语句

查询语句

executeQuery 执行SQL查询语句

注意: 在取第二列的数据的时候,用的是rs.get(2) ,而不是get(1).
这个是整个Java自带的api里唯二的地方,使用基1的,即2就代表第二个。

ResultSet 接口:
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。

package jdbc;

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

public class TestJDBC {
	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
				"root", "admin"); Statement s = c.createStatement();) {

			String sql = "select * from hero";

			// 执行查询语句,并把结果集返回给ResultSet
			ResultSet rs = s.executeQuery(sql);
			while (rs.next()) {
				int id = rs.getInt("id");// 可以使用字段名
				String name = rs.getString(2);// 也可以使用字段的顺序
				float hp = rs.getFloat("hp");
				int damage = rs.getInt(4);
				System.out.printf("%dt%st%ft%d%n", id, name, hp, damage);
			}
			// 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet
			// rs.close();

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}



SQL语句判断账号密码是否正确
  1. 创建一个用户表,有字段name,password
  2. 插入一条数据
insert into user values(null,'dashen','thisispassword');
  1. SQL语句判断账号密码是否正确
    判断账号密码的正确方式是根据账号和密码到表中去找数据,如果有数据,就表明密码正确了,如果没数据,就表明密码错误。

不恰当的方式 是把uers表的数据全部查到内存中,挨个进行比较。 如果users表里有100万条数据呢? 内存都不够用的。


package jdbc;
  
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
  
public class TestJDBC {
    public static void main(String[] args) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
				"root", "admin");
				Statement s = c.createStatement();
				
				) {
            String name = "dashen";
            //正确的密码是:thisispassword
            String password = "thisispassword1";
  
            String sql = "select * from user where name = '" + name +"' and password = '" + password+"'";
             
            // 执行查询语句,并把结果集返回给ResultSet
            ResultSet rs = s.executeQuery(sql);
             
            if(rs.next())
                System.out.println("账号密码正确");
            else
                System.out.println("账号密码错误");
            
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
  
    }
}

获取总数

执行的sql语句为

select count(*) from hero

然后通过ResultSet获取出来

使用PreparedStatement

和 Statement一样,PreparedStatement也是用来执行sql语句的
与创建Statement不同的是,需要根据sql语句创建PreparedStatement
除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接

注: 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的。

package jdbc;
   
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
   
public class TestJDBC {
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
 
        String sql = "insert into hero values(null,?,?,?)";
        try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
        	// 根据sql语句创建PreparedStatement
            PreparedStatement ps = c.prepareStatement(sql);
        ) {
        	
            // 设置参数
            ps.setString(1, "提莫");
            ps.setFloat(2, 313.0f);
            ps.setInt(3, 50);
            // 执行
            ps.execute();
 
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   
    }
}

PreparedStatement的优点1-参数设置

Statement 需要进行字符串拼接,可读性和维护性比较差

String sql = "insert into hero values(null,"+"'提莫'"+","+313.0f+","+50+")";

PreparedStatement 使用参数设置,可读性好,不易犯错

String sql = "insert into hero values(null,?,?,?)";
PreparedStatement有预编译机制,性能比Statement更快

// Statement执行10次,需要10次把SQL语句传输到数据库端
// 数据库要对每一次来的SQL语句进行编译处理
for (int i = 0; i < 10; i++) {
String sql0 = “insert into hero values(null,” + “‘提莫’” + “,”
+ 313.0f + “,” + 50 + “)”;
s.execute(sql0);
}
s.close();

        // PreparedStatement 执行10次,只需要1次把SQL语句传输到数据库端
        // 数据库对带?的SQL进行预编译

        // 每次执行,只需要传输参数到数据库端
        // 1. 网络传输量比Statement更小
        // 2. 数据库不需要再进行编译,响应更快
        for (int i = 0; i < 10; i++) {
            ps.setString(1, "提莫");
            ps.setFloat(2, 313.0f);
            ps.setInt(3, 50);
            ps.execute();
        }
PreparedStatement的优点3-防止SQL注入式攻击

假设name是用户提交来的数据

String name = “‘盖伦’ OR 1=1”;

使用Statement就需要进行字符串拼接
拼接出来的语句是:

select * from hero where name = ‘盖伦’ OR 1=1

因为有OR 1=1,这是恒成立的
那么就会把所有的英雄都查出来,而不只是盖伦
如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来
会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢

   // 假设name是用户提交来的数据
        String name = "'盖伦' OR 1=1";
        String sql0 = "select * from hero where name = " + name;
        // 拼接出来的SQL语句就是
        // select * from hero where name = '盖伦' OR 1=1
        // 因为有OR 1=1,所以恒成立
        // 那么就会把所有的英雄都查出来,而不只是盖伦
        // 如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来
        // 会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢

而PreparedStatement使用的是参数设置,就不会有这个问题

EXECUTE与EXECUTEUPDATe的区别

相同点顶折execute与executeUpdate的相同点:都可以执行增加,删除,修改

不同点

不同1:
execute可以执行查询语句
然后通过getResultSet,把结果集取出来
executeUpdate不能执行查询语句
不同2:
execute返回boolean类型,true表示执行的是查询语句,false表示执行的是insert,delete,update等等
executeUpdate返回的是int,表示有多少条数据受到了影响

获取自增长id

在Statement通过execute或者executeUpdate执行完插入语句后,MySQL会为新插入的数据分配一个自增长id,(前提是这个表的id设置为了自增长,在Mysql创建表的时候,AUTO_INCREMENT就表示自增长)

CREATE TABLE hero ( id int(11) AUTO_INCREMENT, … }

但是无论是execute还是executeUpdate都不会返回这个自增长id是多少。需要通过Statement的getGeneratedKeys获取该id
注: 第20行的代码,后面加了个Statement.RETURN_GENERATED_KEYS参数,以确保会返回自增长ID。 通常情况下不需要加这个,有的时候需要加,所以先加上,保险一些

PreparedStatement ps = c.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);

获取表的元数据

元数据概念:
和数据库服务器相关的数据,比如数据库版本,有哪些表,表有哪些字段,字段类型是什么等等。

// 查看数据库层面的元数据
// 即数据库服务器版本,驱动版本,都有哪些数据库等等

DatabasemetaData dbmd = c.getmetaData();

// 获取数据库服务器产品名称
System.out.println("数据库产品名称:t"+dbmd.getDatabaseProductName());
// 获取数据库服务器产品版本号
System.out.println("数据库产品版本:t"+dbmd.getDatabaseProductVersion());
// 获取数据库服务器用作类别和表名之间的分隔符 如test.user
System.out.println("数据库和表分隔符:t"+dbmd.getCatalogSeparator());
// 获取驱动版本
System.out.println("驱动版本:t"+dbmd.getDriverVersion());

System.out.println("可用的数据库列表:");
// 获取数据库名称
ResultSet rs = dbmd.getCatalogs();

while (rs.next()) {
    System.out.println("数据库名称:t"+rs.getString(1));
}
如何在JDBC中使用事务

不使用事务的情况
没有事务的前提下
假设业务操作是:加血,减血各做一次
结束后,英雄的血量不变
而减血的SQL
不小心写错写成了 updata(而非update)
那么最后结果是血量增加了,而非期望的不变

使用事务

在事务中的多个操作,要么都成功,要么都失败
通过 c.setAutoCommit(false);关闭自动提交
使用 c.commit();进行手动提交
在22行-35行之间的数据库操作,就处于同一个事务当中,要么都成功,要么都失败
所以,虽然第一条SQL语句是可以执行的,但是第二条SQL语句有错误,其结果就是两条SQL语句都没有被提交。 除非两条SQL语句都是正确的。

模板
c.setAutoCommit(false);
程序1
程序2
c.commit();
MYSQL 表的类型必须是INNODB才支持事务

在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务.
修改表的类型为INNODB的SQL:

alter table hero ENGINE  = innodb;

查看表的类型的SQL

show table status from how2java; 

不过有个前提,就是当前的MYSQL服务器本身要支持INNODB,如果不支持,

使用JDBC做一个ORM例子

ORM=Object Relationship Database Mapping
对象和关系数据库的映射
简单说,一个对象,对应数据库里的一条记录

1、根据id返回一个Hero对象
提供方法get(int id)
返回一个Hero对象

package jdbc;
  
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
import charactor.Hero;
  
public class TestJDBC {
  
    public static Hero get(int id) {
        Hero hero = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
			Statement s = c.createStatement();) {

            String sql = "select * from hero where id = " + id;
  
            ResultSet rs = s.executeQuery(sql);
  
            // 因为id是唯一的,ResultSet最多只能有一条记录
            // 所以使用if代替while
            if (rs.next()) {
                hero = new Hero();
                String name = rs.getString(2);
                float hp = rs.getFloat("hp");
                int damage = rs.getInt(4);
                hero.name = name;
                hero.hp = hp;
                hero.damage = damage;
                hero.id = id;
            }
  
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        return hero;
  
    }
  
    public static void main(String[] args) {
          
        Hero h = get(22);
        System.out.println(h.name);
  
    }
}

根据ORM的思想,设计其他几个常见的ORM方法:
把一个Hero对象插入到数据库中
public static void add(Hero h)
把这个Hero对象对应的数据删除掉
public static void delete(Hero h)
更新这条Hero对象
public static void update(Hero h)
把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回
public static List list();

基于JDBC设计DAO的实例

DAO=DataAccess Object数据访问对象

DAO接口

设计类HeroDAO,实现接口DAO

  1. 把驱动的初始化放在了构造方法HeroDAO里:
public HeroDAO() {
	try {
		Class.forName("com.mysql.jdbc.Driver");
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
}

因为驱动初始化只需要执行一次,所以放在这里更合适,其他方法里也不需要写了,代码更简洁

2. 提供了一个getConnection方法返回连接

所有的数据库操作都需要事先拿到一个数据库连接Connection,以前的做法每个方法里都会写一个,如果要改动密码,那么每个地方都需要修改。 通过这种方式,只需要修改这一个地方就可以了。 代码变得更容易维护,而且也更加简洁。

public Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root",
                "admin");
    }
实例 数据库连接池 数据库连接池原理-传统方式

当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。

创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。

同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。

数据库连接池原理-使用池顶折

与传统方式不同,连接池在使用之前,就会创建好一定数量的连接。
如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建.
使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。

ConnectionPool构造方法和初始化
  1. ConnectionPool() 构造方法约定了这个连接池一共有多少连接

  2. 在init() 初始化方法中,创建了size条连接。 注意,这里不能使用try-with-resource这种自动关闭连接的方式,**因为连接恰恰需要保持不关闭状态,**供后续循环使用

  3. getConnection, 判断是否为空,如果是空的就wait等待,否则就借用一条连接出去

  4. returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用notifyAll,通知那些等待的线程,有新的连接可以借用了。

package jdbc;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
 
public class ConnectionPool {
 
    List cs = new ArrayList();
 
    int size;
 
    public ConnectionPool(int size) {
        this.size = size;
        init();
    }
 
    public void init() {
         
        //这里恰恰不能使用try-with-resource的方式,因为这些连接都需要是"活"的,不要被自动关闭了
        try {
            Class.forName("com.mysql.jdbc.Driver");
            for (int i = 0; i < size; i++) {
                Connection c = DriverManager
                        .getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin");
 
                cs.add(c);
 
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
    public synchronized Connection getConnection() {
        while (cs.isEmpty()) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        Connection c = cs.remove(0);
        return c;
    }
 
    public synchronized void returnConnection(Connection c) {
        cs.add(c);
        this.notifyAll();
    }
 
}

测试类

首先初始化一个有3条连接的数据库连接池
然后创建100个线程,每个线程都会从连接池中借用连接,并且在借用之后,归还连接。 拿到连接之后,执行一个耗时1秒的SQL语句。
运行程序,就可以观察到如图所示的效果:

package jdbc;
 
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import jdbc.ConnectionPool;
  
public class TestConnectionPool {
  
    public static void main(String[] args) {
        ConnectionPool cp = new ConnectionPool(3);
        for (int i = 0; i < 100; i++) {
            new WorkingThread("working thread" + i, cp).start();
        }
  
    }
}
  
class WorkingThread extends Thread {
    private ConnectionPool cp;
  
    public WorkingThread(String name, ConnectionPool cp) {
        super(name);
        this.cp = cp;
    }
  
    public void run() {
        Connection c = cp.getConnection();
        System.out.println(this.getName()+ ":t 获取了一根连接,并开始工作"  );
        try (Statement st = c.createStatement()){
            
            //模拟时耗1秒的数据库SQL语句
            Thread.sleep(1000);
            st.execute("select * from hero");
  
        } catch (SQLException | InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        cp.returnConnection(c);
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/331641.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号