JDBC与数据库连接池#yyds干货盘点#
Posted toplabs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC与数据库连接池#yyds干货盘点#相关的知识,希望对你有一定的参考价值。
@TOC
<hr>
1 JDBC
-
> JAVA Database Connectivity java 数据库连接
- 为什么会出现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
- 在src下面声明一个文件 xxx.properties,里面的内容如下:
- insert
INSERT 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("添加失败");
-
delete
DELETE 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("删除失败");
-
query
SELECT * 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);
-
update
UPDATE 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 <Connection> list = new ArrayList<Connection>(); 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 < 5; i++) Connection conn = JDBCUtil.getConn(); list.add(conn); //remove(0) ---> 移除第一个。移除的是集合中的第一个。移除的是开始的那个元素 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 解决自定义数据库连接池出现的问题。
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#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
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```java 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
- 声明一个文件 xxx.properties,里面的内容如下:
- 导入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<Account>() @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>(Account.class), 8);
- 查询多个对象
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
List<Account> list = queryRunner.query("select * from account ",new BeanListHandler<Account>(Account.class));
### 3.3.3 ResultSetHandler 常用的实现类
- 查询单个对象
-
// 以下两个是使用频率最高的 BeanHandler, // 查询到的单个数据封装成一个对象 BeanListHandler, // 查询到的多个数据封装 成一个List<对象> // ------------------------------------------------- ArrayHandler, // 查询到的单个数据封装成一个数组 ArrayListHandler, // 查询到的多个数据封装成一个集合 ,集合里面的元素是数组。 MapHandler, // 查询到的单个数据封装成一个map MapListHandler, // 查询到的多个数据封装成一个集合 ,集合里面的元素是map。
以上是关于JDBC与数据库连接池#yyds干货盘点#的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点# 深入浅出 Spring Boot - 数据访问之 MyBatis
#yyds干货盘点# .NET Core 中对象池(Object Pool)的使用
ceph rados 对象创建以及文件上传与下载#yyds干货盘点#
#yyds干货盘点# springboot配置@Async异步任务的线程池