关于JDBC与数据库连接池,你了解多少?

Ann ·
更新时间:2024-11-13
· 884 次阅读

文章目录1 JDBC1.1 使用JDBC的基本步骤1.2 JDBC工具类构建1.3 数据库的CRUD1.4 Dao模式1.5 Statement安全问题1.6 PrepareStatement2 数据库连接池2.1 自定义数据库连接池2.2 解决自定义数据库连接池出现的问题。2.3 如何扩展某一个方法?3 开源连接池3.1 DBCP3.2 C3P03.3 DBUtils3.3.1 增删改3.3.2 查询3.3.3 ResultSetHandler 常用的实现类 1 JDBC

JAVA Database Connectivity java 数据库连接

为什么会出现JDBC?

SUN公司提供的一种数据库访问规则、规范, 由于数据库种类较多,并且java语言使用比较广泛,sun公司就提供了一种规范,让其他的数据库提供商去实现底层的访问规则。 我们的java程序只要使用sun公司提供的jdbc驱动即可。

1.1 使用JDBC的基本步骤 注册驱动DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 建立连接//第一种 DriverManager.getConnection("jdbc:mysql://localhost/test?user=root&password=root"); //参数一:协议 + 访问的数据库,参数二:用户名,参数三:密码。 //第二种 conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "root"); 创建statement//创建statement,跟数据库打交道,一定需要这个对象 st = conn.createStatement(); 执行sql,得到ResultSet//执行查询,得到结果集 String sql = "select * from stu"; rs = st.executeQuery(sql); 遍历结果集//遍历查询每一条记录 while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id=" + id + "==name==" + name + "==age==" + age); } 释放资源if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { } // ignore rs = null; } ... 1.2 JDBC工具类构建 资源释放工作的整合 驱动防二次注册DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // Driver 这个类里面有静态代码块,一上来就执行了,所以等同于我们注册了两次驱动。 其实没这个必要的。 // 静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); // 最后形成以下代码即可。 Class.forName("com.mysql.jdbc.Driver"); 使用properties配置文件 在src下面声明一个文件 xxx.properties,里面的内容如下: driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/student name=root password=root 在工具类里面,使用静态代码块,读取属性。 // JDBC代码 public class JDBCUtil { static String driverClass = null; static String url = null; static String name = null; static String password= null; static { try { //1. 创建一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); // 使用类加载器,去读取src底下的资源文件 // InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接对象 * @return */ public static Connection getConn() { Connection conn = null; try { Class.forName(driverClass); conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs) { try { if (rs != null) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } finally { rs = null; } } private static void closeSt(Statement st) { try { if (st != null) { st.close(); } } catch (SQLException e) { e.printStackTrace(); } finally { st = null; } } private static void closeConn(Connection conn) { try { if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } finally { conn = null; } } } 1.3 数据库的CRUD insertINSERT INTO t_stu (name, age) VALUES ('wangqiang', 28); INSERT INTO t_stu VALUES (NULL, 'wangqiang2', 28); // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "insert into t_stu values(null , 'aobama' , 59)"; //影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) { System.out.println("添加成功"); } else { System.out.println("添加失败"); } deleteDELETE FROM t_stu WHERE id = 6; // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "delete from t_stu where name='aobama'"; // 影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) { System.out.println("删除成功"); } else { System.out.println("删除失败"); } querySELECT * FROM t_stu; // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行sql语句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍历结果集 while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + " " + age); } updateUPDATE t_stu SET age = 38 WHERE id = 1; // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "update t_stu set age = 26 where name ='qyq'"; // 影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) { System.out.println("更新成功"); } else { System.out.println("更新失败"); } 1.4 Dao模式

Data Access Object 数据访问对象

新建一个dao的接口,里面声明数据库访问规则

/** * 定义操作数据库的方法 */ public interface UserDao { /** * 查询所有 */ void findAll(); }

新建一个dao的实现类,具体实现早前定义的规则

public class UserDaoImpl implements UserDao{ @Override public void findAll() { Connection conn = null; Statement st = null; ResultSet rs = null; try { //1. 获取连接对象 conn = JDBCUtil.getConn(); //2. 创建statement对象 st = conn.createStatement(); String sql = "select * from t_user"; rs = st.executeQuery(sql); while(rs.next()){ String userName = rs.getString("username"); String password = rs.getString("password"); System.out.println(userName+"="+password); } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, st, rs); } } }

直接使用实现

@Test public void testFindAll(){ UserDao dao = new UserDaoImpl(); dao.findAll(); } 1.5 Statement安全问题 Statement执行,其实是拼接sql语句的。先拼接sql语句,然后再一起执行。String sql = "select * from t_user where username='"+ username +"' and password='"+ password +"'"; UserDao dao = new UserDaoImpl(); dao.login("admin", "100234khsdf88' or '1=1"); // 一定可以登录成功 // SELECT * FROM t_user WHERE username='admin' AND PASSWORD='100234khsdf88' or '1=1' // 前面先拼接sql语句,如果变量里面带有了数据库的关键字,那么一并认为是关键字。不认为是普通的字符串。 rs = st.executeQuery(sql); 1.6 PrepareStatement

该对象就是替换前面的statement对象。

相比较以前的statement, 预先处理给定的sql语句,对其执行语法检查。在sql语句里面使用 ? 占位符来替代后续要传递进来的变量。后面进来的变量值,将会被看成是字符串,不会产生任何的关键字。

String sql = "insert into t_user values(null , ? , ?)"; ps = conn.prepareStatement(sql); //给占位符赋值 从左到右数过来,1 代表第一个问号,从1开始。 ps.setString(1, userName); ps.setString(2, password); 2 数据库连接池

数据库的连接对象创建工作,比较消耗性能。

一开始先在内存中开辟一块空间(集合),一开先往池子里面放置多个连接对象。后面需要连接的话,直接从池子里面取。不要去自己创建连接了。使用完毕,要记得归还连接。确保连接对象能循环利用。

2.1 自定义数据库连接池 代码实现/* * 1. 开始创建10个连接。 * 2. 来的程序通过getConnection获取连接 * 3. 用完之后,使用addBack归还连接。 * 4. 扩容。 */ public class MyDataSource implements DataSource { List list = new ArrayList(); public MyDataSource() { for (int i = 0; i < 10; i++) { Connection conn = JDBCUtil.getConn(); list.add(conn); } } // 该连接池对外公布的获取连接的方法 @Override public Connection getConnection() throws SQLException { //来拿连接的时候,先看看,池子里面还有没有。 if(list.size() == 0 ){ for (int i = 0; i 移除第一个。移除的是集合中的第一个。移除的是开始的那个元素 Connection conn = list.remove(0); return conn; } /** * 用完之后,记得归还。 * @param conn */ public void addBack(Connection conn){ list.add(conn); } ...... } 出现的问题: 需要额外记住 addBack方法 无法面向接口编程。UserDao dao = new UserDaoImpl(); dao.insert(); DataSource dataSource = new MyDataSource(); // 因为接口里面没有定义addBack方法。 怎么解决? 以addBack 为切入点。 2.2 解决自定义数据库连接池出现的问题。

由于多了一个addBack方法,所以使用这个连接池的地方,需要额外记住这个方法,并且还不能面向接口编程。

我们打算修改接口中的那个close方法。原来的Connection对象的close方法,是真的关闭连接。
打算修改这个close方法,以后在调用close,并不是真的关闭,而是归还连接对象。

2.3 如何扩展某一个方法?

原有的方法逻辑,不是我们想要的。想修改自己的逻辑

直接改源码 -> 无法实现。 继承 -> 必须得知道这个接口的具体实现是谁。 使用装饰者模式。 3 开源连接池 3.1 DBCP 导入jar文件 不使用配置文件:public void testDBCP01() { Connection conn = null; PreparedStatement ps = null; try { // 1. 构建数据源对象 BasicDataSource dataSource = new BasicDataSource(); // 连的是什么类型的数据库, 访问的是哪个数据库,用户名,密码。。 // jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost/bank"); dataSource.setUsername("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admin"); ps.setInt(2, 1000); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtil.release(conn, ps); } } 使用配置文件方式: 声明一个文件 xxx.properties,里面的内容如下: # 连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/bank username=root password=root # initialSize=10 #最大连接数量 maxActive=50 # maxIdle=20 # minIdle=5 # maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useUnicode=true;characterEncoding=gbk #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED Connection conn = null; PreparedStatement ps = null; try { BasicDataSourceFactory factory = new BasicDataSourceFactory(); Properties properties = new Properties(); InputStream is = new FileInputStream("src//dbcpconfig.properties"); properties.load(is); DataSource dataSource = factory.createDataSource(properties); //2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null, ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "liangchaowei"); ps.setInt(2, 100); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.release(conn, ps); } 3.2 C3P0

导入jar文件

不使用配置文件方式

Connection conn = null; PreparedStatement ps = null; try { // 1. 创建datasource ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 2. 设置连接数据的信息 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); dataSource.setUser("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admi234n"); ps.setInt(2, 103200); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.release(conn, ps); }

使用配置文件方式

// 默认会找 xml 中的 default-config 分支。 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 1. 设置连接数据的信息 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); dataSource.setUser("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admi234n"); ps.setInt(2, 103200); 3.3 DBUtils 3.3.1 增删改 //dbutils 只是帮我们简化了CRUD 的代码, 但是连接的创建以及获取工作。 不在它的考虑范围 QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); //增加 queryRunner.update("insert into account values (null , ? , ? )", "aa" ,1000); //删除 queryRunner.update("delete from account where id = ?", 5); //更新 queryRunner.update("update account set money = ? where id = ?", 10000000 , 6); 3.3.2 查询 直接new接口的匿名实现类QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler(){ @Override public Account handle(ResultSet rs) throws SQLException { Account account = new Account(); while(rs.next()){ String name = rs.getString("name"); int money = rs.getInt("money"); account.setName(name); account.setMoney(money); } return account; } }, 6); System.out.println(account.toString()); 直接使用框架已经写好的实现类。 查询单个对象QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); //查询单个对象 Account account = queryRunner.query("select * from account where id = ?", new BeanHandler(Account.class), 8); 查询多个对象QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); List list = queryRunner.query("select * from account ",new BeanListHandler(Account.class)); 3.3.3 ResultSetHandler 常用的实现类 // 以下两个是使用频率最高的 BeanHandler, // 查询到的单个数据封装成一个对象 BeanListHandler, // 查询到的多个数据封装 成一个List // ------------------------------------------------- ArrayHandler, // 查询到的单个数据封装成一个数组 ArrayListHandler, // 查询到的多个数据封装成一个集合 ,集合里面的元素是数组。 MapHandler, // 查询到的单个数据封装成一个map MapListHandler, // 查询到的多个数据封装成一个集合 ,集合里面的元素是map。
作者:ityanger



连接 数据库连接 数据 数据库连接池 jdbc 连接池 数据库

需要 登录 后方可回复, 如果你还没有账号请 注册新账号