1、简单认识一下JDBC
- 1)、JDBC是什么?
- java database connection java数据库连接
- 作用:就是为了java连接mysql数据库嘛
- 要详细的,就面向百度编程吧
-
2)、JDBC是一种驱动,那么它位于哪个地方?
-
-
3)、JDBC的实现 ———— 五步骤
- 加载驱动
- 获取连接 driverManager
- 获取执行sql的对象 statement / PreparedStatement
- 获取结果集 resultSet
- 释放资源
-
-
快速上手操作一下
- (1)、准备工作 —— 下载JDBC
-
快速上手操作一下
-
-
-
- 注意选择自己的版本:
-
-
-
-
-
-
再来数据库创建一个数据库 和 表
-
CREATE DATABASE IF NOT EXISTS jdbc; USE jdbc; CREATE TABLE IF NOT EXISTS person( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(20) NOT NULL, sex CHAR(2) NOT NULL, age INT NOT NULL )ENGINE = INNODB DEFAULT CHARSET = utf8; INSERT INTO person VALUES( NULL , \'紫邪情\' , \'女\' , \'18\' ), ( NULL , \'君莫邪\' , \'男\' , \'19\' ), ( NULL , \'张雪\' , \'女\' , \'19\' ), ( NULL , \'韩非\' , \'男\' , \'19\' );
-
-
再来数据库创建一个数据库 和 表
-
-
-
-
-
-
然后把下载的JDBC弄到java项目中去
-
标记文件类型
-
标记成功之后jar包前面有一个 > 符号
-
-
-
然后把下载的JDBC弄到java项目中去
-
-
-
-
-
(2)、利用五步骤。开始测试
-
package cn.xieGongZi.test; import java.sql.*; public class PlayJDBC { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 1、加载驱动 利用了反射 Class.forName("com.mysql.jdbc.Driver"); // 这个是死的 这里为了看逻辑,所以把异常抛出 /* * jdbc:mysql: 是指协议 这是固定写法 * localhost 是主机地址 这里使用127.0.0.1也可以,要是想连接别人电脑上的数据库,这里把ip地址换了就可以了 * 3306 指:sql的端口号 * jdbc 就是要链接的数据库 * ? 表示要跟参数 即:? 后面的就是参数 * characterEncoding 指:设置字符编码 其他的参数都可以少,这个最好别少 * useUnicode 使用Unicode编码格式 * useSSL 使用安全套接层协议 即:安全连接 * * 因此:这个url的写法就出来了: * 协议 : // 主机地址 : 端口号 / 数据库名 ? 参数 * */ String url = "jdbc:mysql://localhost:3306/jdbc? characterEncoding = utf-8 && useUnicode = true && useSSL = true"; String username = "root"; // 这是数据库的用户名 String password = "072413"; // 这是数据库的密码 要操作数据库肯定要登进去涩,所以:路径是什么?用户名和密码时什么肯定需要告知嘛 // 2、获取连接 使用DriverManager 这是java.sql包下的 Connection con = DriverManager.getConnection(url, username, password); // 发现这个getConnection需要三个参数 url 、 username 、 password,所以建一下,需要处理异常,为了好看逻辑,照样跑出去 // 3、通过链接对象 去 获取执行sql的对象 Statement st = con.createStatement(); String sql = "select * from person"; // 在这个地方有些人可能会出现 表名 识别不了 那就用 库名.表名 如:jdbc.person // 这里进行查询操作 注:在java中, 增删改 是executeUpdate( String sql )方法 查询是executeQuery( String sql )方法 // 4、执行sql,获取结果集 ResultSet rs = st.executeQuery(sql); // 发现这个方法需要一个sql , 那么编写一句 // sql语句弄好了,结果集resultSet也得到了,但是看名字就知道ResultSet肯定是一个容器嘛,所以得把结果拿出来涩 // 使用一个方法 next() 这是看下一行是否有数据 怎么理解呢? // 就是去读写数据时,有一个指针,这个指针是指向数据库表的表头的,即:列字段哪一行 什么name、age、sex....这一行 // 所以:去一行一行读写时,是看下一行是否有数据 while ( rs.next() ){ // 当然:要是数据只有一行的话 用个if就可以了 System.out.print( rs.getInt("id") + " " ); System.out.print( rs.getString("name") + " " ); System.out.print( rs.getString("sex") + " " ); System.out.println( rs.getInt("age") + " " ); } // 5、释放资源 ———— 倒着关闭 if ( rs != null ){ // 在这里这个if不要都行,为了后续代码的健壮性,严谨,所以这里加了一个判断罢了 rs.close(); } if ( st != null ){ st.close(); } if ( con != null ){ con.close(); } } }
-
-
(2)、利用五步骤。开始测试
-
效果如下( 成功从数据库中拿到数据了 ):
-
对JDBC的总结和补充
-
JDBC的步骤
- 加载驱动 Class.forName("com.mysql.jdbc.Driver");
- 获取连接 Connection con = DriverManager.getConnection( String url , String username , String password );
- 获取执行sql的对象 Statement st = con.CreateStatement();
- 获取结果集 / 获取受影响的行数( 这个是增删改的时候的结果 ) ResultSet rs = st.executeQuery( String sql ); 增删改就是executeUpdate( String sql )
- 释放资源 close()
-
JDBC的步骤
-
-
对一些方法的补充:
-
Connection中常用的方法
-
这就是一个连接对象嘛,它代表的就是数据库。所以它可以设置数据库中的任何东西
- rollback 回滚
- autocommit 自动提交
- commit 提交事务
- createStatement 创建执行sql的对象 —— 提前讲一下:这个对象不安全
- preparedStatement 这个也是创建执行sql的对象 ———— 这个更安全,后续会说明
- close 关闭连接
- 其他的方法 直接通过 Connection. 之后就可以看到它所有的方法了
-
这就是一个连接对象嘛,它代表的就是数据库。所以它可以设置数据库中的任何东西
-
Connection中常用的方法
-
对一些方法的补充:
-
-
-
Statement中常用方法
- executeQuery 执行sql查询语句 返回的是一个结果集 ResultSet
- executeUpdate 执行sql增 / 删 / 改 语句 返回的是受影响的行数
- execute 执行任何的sql语句
- close 关闭资源
-
Statement中常用方法
-
-
-
-
ResultSet中常用方法
- getObject 在不知道数据库中字段的类型时采用,获取任意类型嘛
- 获取对应类型
- getString
- getInt
- getDouble
- ........
- 移动指针
- beforeFirst 移动到最前面 即:表头的位置
- afterLast 移动到最后面 即:数据的最后一行
- next 移动到下一行数据
- previous 移动到前一行数据
- absolute( int row ) 移动到指定行
-
关闭资源
- close
-
ResultSet中常用方法
-
-
4)、JDBC的封装
-
为什么需要封装?
- 前面那个JDBC五步骤是固定的,那么每有一个类需要进行操作数据库时不得都要写一次吗,那里面有很多共同的代码,只有个别不同而已,所以:不就可以提取成一个工具类吗
-
为什么需要封装?
-
-
(1)、自己进行的封装
-
①、简单封装
-
加入实体类 —— 即:sql中的一张表对应java中的一个实体类 实体类的包名为 pojo / vo / entity 都可以
-
-
加入实体类 —— 即:sql中的一张表对应java中的一个实体类 实体类的包名为 pojo / vo / entity 都可以
-
①、简单封装
-
(1)、自己进行的封装
-
-
-
-
package cn.xieGongZi.pojo; // 加入对应数据库中的列字段 成为 属性 // 加入get 和 set 方法 // 加入toString方法 public class Person { private int id; private String name; private String sex; private int age; @Override public String toString() { return "Person{" + "id=" + id + ", name=\'" + name + \'\\'\' + ", sex=\'" + sex + \'\\'\' + ", age=" + age + \'}\'; } public Person() { } public Person(int id, String name, String sex, int age) { this.id = id; this.name = name; this.sex = sex; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
实体类完毕!
-
-
-
-
-
-
-
封装JDBC
-
建一个utils包
-
-
建一个utils包
-
封装JDBC
-
-
-
-
-
-
-
开始封装JDBC
-
package cn.xieGongZi.utils; import java.sql.*; public class JDBCUtils { private static String url; private static String username; private static String password; static { try { // 加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 编写url、username、password url = "jdbc:mysql://localhost:3306/jdbc?characterEncoding = utf-8 && useUnicode = true && useSSL = true "; username = "root"; password = "072413"; } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 1、设置获取链接方法 public static Connection getConnection() throws SQLException { return DriverManager.getConnection( url , username , password ); } // 2、封装释放资源方法 public static void release(Connection con , Statement st , ResultSet rs ){ if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( st != null ){ try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( con != null ){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } // 3、在这里 再把增删改 和 查询方法封装起来也行 目前不演示了 }
-
-
开始封装JDBC
-
-
-
-
-
-
-
-
通过加入实体类,测试封装的JDBC
-
编写数据操作层
-
编写数据操作层
-
通过加入实体类,测试封装的JDBC
-
-
-
-
-
-
-
-
-
测试一下:
package cn.xieGongZi.dao; import cn.xieGongZi.pojo.Person; import cn.xieGongZi.utils.JDBCUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class PersonDao { public List<Person> getAllPerson(){ // 创建一个集合 用来装查询的结果 从情况来看:ArrayList集合更适合 因为:有可能查出来的数据有重复的、而且最好有序 ArrayList<Person> list = new ArrayList<>(); // 1、加载驱动、获取链接已经在工具类中封装了 所以:直接拿来用
Connection con = null; // 提示作用域 因为:放在try里面的话,在后面释放资源哪里拿不到这三个参数
Statement st = null;
ResultSet rs = null; try { con = JDBCUtils.getConnection(); // 2、获取操作执行sql的对象 st = con.createStatement(); // 编写sql语句 String sql = "select id , `name` , sex , age from person"; // 3、执行sql语句 , 获得结果集 rs = st.executeQuery(sql); // 加入了实体类 所以:需要把结果集中的数据 弄到 实体类中去 // 所以:还得有个实体类诶 Person people = new Person(); while ( rs.next() ){ // 遍历结果集 准备把数据 放到 实体类中 people.setId( rs.getInt("id") ); people.setName( rs.getString("name") ); people.setSex( rs.getString("sex") ); people.setAge( rs.getInt("age") ); // 最后:把people 装到 list集合中去 一开始玩这一步不要都行 // 只是:这里是为了给后面的三层架构打基础 list.add( people ); } } catch (SQLException e) { e.printStackTrace(); }finally{
JDBCUtils.release( con , st , rs ); // 释放资源 有null的,在JDBCUtils中会自行判断
} return list; // 这里返回,然后在其他地方也就可以拿到这个结果了 } }
-
-
-
-
-
-
-
-
-
-
②、上面的封装还是麻烦,不太满意,那就再来封装
-
一样的,先建好对应的包 写好对应的东西:实体类 当然:不用我这么建也可以,根据自己想法来
-
-
一样的,先建好对应的包 写好对应的东西:实体类 当然:不用我这么建也可以,根据自己想法来
-
②、上面的封装还是麻烦,不太满意,那就再来封装
-
-
-
-
-
-
-
-
-
在玩javaSE的集合时,不是弄过一个hashtable的儿子 —— Properties吗,来用一下它
-
-
在玩javaSE的集合时,不是弄过一个hashtable的儿子 —— Properties吗,来用一下它
-
-
-
-
-
-
-
-
-
-
继续封装JDBC
-
package cn.xieGongZi.utils; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class JDBCUtil { // 把driver、url、username、password放到了properties中了,那么就加载出来 private static String driver; private static String url; private static String username; private static String password; static { // 通过类加载器加载出db.properties —— 输入流嘛 InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("cn/xieGongZi/resources/db.properties"); // 创建一个properties对象 Properties ppt = new Properties(); // 把流管道中的东西加载到properties中来 try { ppt.load(in); // 把装到properties中的东西 获取出来 driver = ppt.getProperty("driver"); url = ppt.getProperty("url"); username = ppt.getProperty("username"); password = ppt.getProperty("password"); // 顺便做一件事情:加载驱动 Class.forName( driver ); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } // 1、封装获取数据库链接对象的方法 这样别人只需要用我提供的其他方法即可 private static Connection getConnection() throws SQLException { return DriverManager.getConnection( url , username , password ); } // 2、提供释放资源的方法 public static void release(Connection con , Statement st , ResultSet rs ){ if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( st != null ){ try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( con != null ){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } // 3、把增删改的方法也封装了 public static int update( String sql ) throws SQLException { // 原生的JDBC在增删改语句执行之前做了什么? // 1、加载驱动 —— 前面已经做了 // 2、获取数据库链接对象 —— 前面封装了 所以直接拿来用 Connection con = getConnection();// 异常抛出去 // 3、通过数据库链接对象 获取 执行sql的对象 Statement st = con.createStatement(); // 4、执行sql 得到受影响的行数 int rowCounts = st.executeUpdate(sql); return rowCounts; // 这里可以一步到位:return st.executeUpdate( sql ) ; } // 4、继续封装 把查询方法也封装了 public static ResultSet query( String sql ) throws SQLException { // 前面都是一样的涩 复制过来 // 原生的JDBC在增删改语句执行之前做了什么? // 1、加载驱动 —— 前面已经做了 // 2、获取数据库链接对象 —— 前面封装了 所以直接拿来用 Connection con = getConnection();// 异常抛出去 // 3、通过数据库链接对象 获取 执行sql的对象 Statement st = con.createStatement(); // 4、执行sql 返回结果集 return st.executeQuery(sql); } }
-
-
继续封装JDBC
-
-
-
-
-
-
-
-
-
-
封装好了,那来测试一下
-
编写数据操作层 即:dao层
-
效果如下:
package cn.xieGongZi.dao; import cn.xieGongZi.pojo.Person; import cn.xieGongZi.utils.JDBCUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class Persondao { public List<Person> getAllPerson() { ArrayList<Person> list = new ArrayList<>(); // 原生JDBC前面做了什么? // 1、加载驱动 —— 在JDBC工具类中已经封装了 // 2、获取数据库链接对象 ———— 还是在JDBC中封装了 这里可用可不用 —— 看封装增删查改方法的过程 // 我在增删查改中是直接调用了 getConnection() 不是当参数传进去了,所以:这里直接不用调,因为:增删改查中调用了 // 编写sql语句 String sql = " select id , `name` , sex , age from person"; // 3、获取执行sql的对象 返回结果集 —— 封装了 直接调用 ResultSet rs = null; Statement st = null; Connection con = null; try { rs = JDBCUtil.query(sql); // 拿到结果集了 那么把结果集中的数据放到list集合中 // 还是需要一个Person实体类对象 Person person = new Person(); while ( rs.next() ){ person.setId( rs.getInt("id") ); person.setName( rs.getString("name") ); person.setSex( rs.getString("sex") ); person.setAge( rs.getInt("age") ); // 把person放到list中 这样就可以在其他地方也拿到了 list.add( person ); } } catch (SQLException e) { e.printStackTrace(); }finally { // 最后释放资源 JDBCUtil.release( con , st , rs ); // 发现这里拿不到ResultSet 那么提升作用域 // 发现需要Connection 、 Statement对象,那就创建一下,让它等于null 这样在JDBCUtil中会自行判断 } return list; } } -
-
-
编写数据操作层 即:dao层
-
封装好了,那来测试一下
-
-
-
-
-
-
-
-
-
-
-
-
最后:这你妹嘞个巴子滴,和前面的封装不是一样的吗?
- 不一样,这种是建议用的一种( 是抛开后面用的连接池啊 ),因为这种速度更快一点,以及分工更明确一点( 更像编程思维嘛 )
-
最后:这你妹嘞个巴子滴,和前面的封装不是一样的吗?
-
-
-
-
-
-
-
-
-
-
-
③、前面都是用的Statement这个执行sql的对象,但是:一开始我便提过一点 这个对象不安全,因为:它不能防止SQL注入
-
SQL注入是什么意思?
- 简单理解就是:这是SQL的一个漏洞,别人在攻击数据库时,可以通过逻辑正确,但非法的方式进入到数据库中,从而导致:数据泄露
-
SQL注入是什么意思?
-
③、前面都是用的Statement这个执行sql的对象,但是:一开始我便提过一点 这个对象不安全,因为:它不能防止SQL注入
-
-
-
-
-
-
-
-
-
来体验一下
-
一样的,把对应的包创建好
-
一样的,把对应的包创建好
-
来体验一下
-
-
-
-
-
-
-
-
-
-
-
简单模仿一下登录业务流程
-
建一个用户表
-
CREATE TABLE IF NOT EXISTS `user`( username VARCHAR(20) NOT NULL, `password` VARCHAR(10) NOT NULL )ENGINE = INNODB DEFAULT CHARSET = utf8; INSERT INTO `user` VALUES( \'紫邪情\' , \'072413\' ), ( \'君莫邪\' , \'123456\' ), ( \'韩非\' , \'774931\' );
-
-
建一个用户表
-
简单模仿一下登录业务流程
-
-
-
-
-
-
-
-
-
-
-
-
-
效果如下:
package cn.xieGongZi.test; import cn.xieGongZi.utils.JDBCUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Demo { public static void login( String username , String password ){ // 编写sql String sql = " select * from `user` where username = \' "+username+" \' and password = \' "+password+" \' "; // 因为转义字符的关系 所以:这里的\' "+usernmae+" \' 在外面加了一个 `` 飘字符 // 执行sql 获得结果集 Connection con = null; // 提升作用域 Statement st = null; ResultSet rs = null; try { rs = JDBCUtil.query(sql); while ( rs.next() ){ System.out.println( rs.getString("username") ); System.out.println( rs.getString("password") ); } } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtil.release( con , st , rs ); } } // 测试一下 public static void main(String[] args) { // 模仿一下登录业务流程 // 前面第二种封装就是使用的Statement对象 login( " \'or\' 1=1 " , " \'or\' 1=1 "); // 这里使用逻辑正确,但是:违法的技巧 } }- 成功达到目的,因此:Statement不建议使用,不安全,最好使用我接下来要说明的这个对象 PreparedStatement。
- 当然:要是有人整 —— 我前面用的db.properties中有数据库用户名 和 密码,所以可以做到,那就别扯了,这是两码事儿。
- 因此:要是有人说用statement这个执行sql语句的对象的话,那他就是个草包,听都别听他鬼扯。
-
-
-
-
-
-
-
-
-
-
-
-
-
PreparedStatement对象 —— 预编译对象
- 前面的流程都一样 使用 添加实体类 、 db.properties 所以不演示了,主要从封装JDBC开始,把Statement对象换了
-
PreparedStatement对象 —— 预编译对象
-
-
-
-
-
-
-
-
-
-
- JDBC的封装
-
package cn.xieGongZi.utils; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class JDBCUtil { private static String driver; private static String url; private static String username; private static String password; static { InputStream ins = JDBCUtil.class.getClassLoader().getResourceAsStream("cn/xieGongZi/resources/db.properties"); Properties ppt = new Properties(); try { ppt.load( ins ); driver = ppt.getProperty("driver"); url = ppt.getProperty("url"); username = ppt.getProperty("username"); password = ppt.getProperty("password"); Class.forName(driver); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } // 封装获取数据库链接对象 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } // 提供释放资源的方法 public static void release(ResultSet rs , PreparedStatement ps , Connection con ) { // 注意:这里是用的PreparedStatement对象 if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( ps != null ){ try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( con != null ){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
数据操作层
-
效果如下:
package cn.xieGongZi.dao; import cn.xieGongZi.pojo.Person; import cn.xieGongZi.utils.JDBCUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class PersonDao { public List<Person> getAllPerson() { ArrayList<Person> list = new ArrayList<>(); // 提升作用域 Connection con = null; PreparedStatement ps = null; // 使用preparedStatement对象 ResultSet rs = null; // 还是一样 获取链接 try { con = JDBCUtil.getConnection(); // 编写sql语句 // 因为:使用的是preparedStatement对象,所以这里有点区别 String sql = " select * from person where `name` = ? "; // 使用占位符 ? 来代替要赋的值 在后面再给它赋值 // 执行sql ps = con.prepareStatement(sql); // 对占位符进行赋值 ps.setString(1,"紫邪情"); // 这表示给第一个参数( 即:? ),有多个的话,数值依次往后,跟着赋值即可 // 现在再开始执行sql 得到结果集 rs = ps.executeQuery(); Person people = new Person(); while ( rs.next() ){ people.setId( rs.getInt("id")); people.setName( rs.getString("name")); people.setSex( rs.getString("sex")); people.setAge( rs.getInt("age")); list.add( people ); } } catch (SQLException e) { e.printStackTrace(); }finally{
JDBCUtil.release( rs , ps , con );
} return list; } }
-
-
-
-
-
-
-
-
(2)、使用别人封装好的 ———— 连接池
-
①、什么是连接池?
-
在javaSE的多线程中说过一个线程池,对照起来看
- 池,就是池化技术嘛,一个容器咯,而这个容器的作用就是:控制连接数据库的个数、设置url、username、password、连接数量不够进行扩容之类的容器嘛
- 数据库连接 -------> 执行完毕 ---------> 释放资源 整个过程是很消耗资源的。因此:连接池的真正作用就是 预先准备一些资源,这样一过来就可以进行连接操作了
-
在javaSE的多线程中说过一个线程池,对照起来看
-
①、什么是连接池?
-
(2)、使用别人封装好的 ———— 连接池
-
-
-
-
怎么编写连接池?
-
很简单,只需要实现一个类就可以了 DataSource 去看一下这个类的源码( 是java.sql包下的 )
-
但是啊:有别人写好的东西,我们不用手写,因此:拿别人写好的东西来用一下咯
-
-
很简单,只需要实现一个类就可以了 DataSource 去看一下这个类的源码( 是java.sql包下的 )
-
怎么编写连接池?
-
-
-
-
-
②、别人写好的连接池有哪些?
- 最流行的就是:c3p0 、 dbcp 、 阿里巴巴的druid
-
②、别人写好的连接池有哪些?
-
-
-
-
-
简单聊一下这三个的理论知识
-
druid 是淘宝 和 支付宝的必用数据库连接池 这玩意儿主要就是可以做到监控的功能,详细点就是如下:
- 对Oracle 和 MySQL做了特别的优化 如:Oracle 的 PS Cache 内存占用优化,MySql 的 ping 检测优化
- 使得分析 SQL 的抽象语法树很方便
- 简单 SQL 语句用时 10 微秒以内,复杂 SQL 用时 30 微秒
- 通过 Druid 提供的 Parser 可以在 JDBC 层拦截 SQL 做相应处理 ( 比如:分库分表,审计等 )
- 上面这些更细的知识点不懂没关系,只需要知道主要是搞监控机制的就行
-
druid 是淘宝 和 支付宝的必用数据库连接池 这玩意儿主要就是可以做到监控的功能,详细点就是如下:
-
简单聊一下这三个的理论知识
-
-
-
-
-
-
-
DBCP
- 是一个依赖 jakarta commons-pool 对象池机制的数据库连接池,DBCP,也是 Tomcat 使用的连接池组件
-
DBCP
-
-
-
-
-
-
-
-
c3p0
- 是一个开放源代码的 JDBC 连接池,它在 lib 目录中与 Hibernate 一起发布,,包括了实现 jdbc3 和 jdbc2 扩展规范说明的 Connection 和 Statement 池的 DataSources 对象
-
c3p0
-
-
-
-
-
-
-
-
-
多说一嘴:DBCP和c3p0的区别
- ①、dbcp 没有自动回收空闲连接功能,c3p0 有自动回收空闲连接功能
②、两者对数据连接处理方式不同:c3p0 提供最大空闲时间,dbcp 提供最大连接数
③、c3p0 连接超过最大连接时间时,当前连接会断掉。dbcp 当连接数数超过最大连接数时,所有连接都会被断开
④、dbcp 它的原理是维护多个连接对象 Connection。在 web 项目要连接数据库时直接使用它维护对象进行连接,省去每次都要创建对象的麻烦,提高平效率和减少内存使用
⑤、c3p0 可以自动回收连接,dbcp 需要自己手动释放资源返回,不过 dbcp 效率比较高
- ①、dbcp 没有自动回收空闲连接功能,c3p0 有自动回收空闲连接功能
-
多说一嘴:DBCP和c3p0的区别
-
-
-
-
-
-
-
-
-
壹、一法通、万法通,先来玩一下DBCP
-
①、需要两个jar包
- 一个commons-dbcp 一个 commons-pool 想要什么版本自己去找,我的版本如下:
- commons-dbcp-1.4
- commons-pool-1.6
-
老规矩:把对应的jar包放到项目中去
-
①、需要两个jar包
-
壹、一法通、万法通,先来玩一下DBCP
-
-
-
-
-
-
-
-
-
②、需要设置配置文件
-
玩儿DBCP需要使用Properties进行编写配置
-
# 连接设置
# 注意:这个driverClassName必须用这个名字,别用什么driver就完了 因为:底层中找的就是这个名字 driverClassName = com.mysql.jdbc.Driver url = jdbc:mysql://localhost:3306/jdbc?characterEncoding = utf-8 & userUnicode = true & useSSL = true username = root password = 072413 # 初始化连接数量 initialSize = 10 # 最大连接数量 maxActive = 50 # 最大空闲连接数 maxIdle = 20 # 最小空闲连接数 minIdle = 5 # 超时等待时间 , 以毫秒为单位 maxWait = 60000 # 下面的设置要不要都无所谓 ## JDBC驱动建立连接时附带的连接属性的格式必须为这样 ———— 属性名 = property ## 另外:user 和 password两个属性会被明确地传递,因此:这里不需要包含他们 #connectionProperties = useUnicode = true ; characterEncoding = utf-8 # ## 指定有连接池所创建的连接的自动提交 auto-commit状态 #defaultAutoCommit = true # ## driver default 指定由连接池所创建的连接的只读 read-only状态 ## 但是:如果没有设置该值,则:setReadOnly方法不被调用 ## 另外:某些驱动并不支持只读模式 如:informix ## 因此:这个不设置也行 ## defaultReadOnly = ...... # ## driver default 指定有连接池所创建的连接的事务级别 transactionIsolation ## 可用值为下列之一: ## NONE , READ_UNCOMMITTED , READ_COMMITTED , REPEATABLE_READ #defalutTransactionIsolation = READ_UNCOMMITTED # # ## 有需要的配置,没需要的就不配置这中间的东西,我嫌麻烦,不配置了,直接注释掉
配置好了,那得用一下咯
-
-
玩儿DBCP需要使用Properties进行编写配置
-
②、需要设置配置文件
-
-
-
-
-
-
-
-
-
-
-
实体类还是用的person、util 和 dao需要做一下调整
-
封装DBCP工具类
-
延伸一下:不是有一个BasicDataSourceFactory吗,看一下它的源码:
package cn.xieGongZi.utils; import org.apache.commons.dbcp.BasicDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class JDBCUtil { // private static String driver; // 相应的这些也不要了 // private static String url; // private static String username; // private static String password; private static DataSource dataSource = null; // 提升作用域 为了获取链接嘛 static { // 这里就是去加载 DBCP_pool.properties 了 InputStream ins = JDBCUtil.class.getClassLoader().getResourceAsStream("cn/xieGongZi/resources/DBCP_pool.properties"); Properties ppt = new Properties(); try { ppt.load( ins ); // driver = ppt.getProperty("driver"); // 这一部分就不需要了,因为:在配置文件中配置了 // url = ppt.getProperty("url"); // username = ppt.getProperty("username"); // password = ppt.getProperty("password"); // Class.forName(driver); // 这里需要借助一个类来获取数据源 这是一个工厂模式 这个是用来创建对象的 它需要一个Properties对象,所以直接扔给它 dataSource = BasicDataSourceFactory.createDataSource(ppt);// 处理异常 这个东西获取的是一个Datasource数据源 } catch (Exception e) { e.printStackTrace(); } } // 封装获取数据库链接对象 public static Connection getConnection() throws SQLException { // return DriverManager.getConnection(url, username, password); // 前面看源码不是看过datasource中自带获取连接吗,所以:这里直接通过DataSource获取连接 return dataSource.getConnection(); // 这样就把DBCP的工具类封装好了 } // 释放资源方法还是一样需要 // 提供释放资源的方法 public static void release(ResultSet rs , PreparedStatement ps , Connection con ) { // 注意:这里是用的PreparedStatement对象 if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( ps != null ){ try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( con != null ){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
-
封装DBCP工具类
-
实体类还是用的person、util 和 dao需要做一下调整
-
-
-
-
-
-
-
-
-
-
-
-
-
修改dao层 其实就改两个地方 使用工具类调用那里
-
-
修改dao层 其实就改两个地方 使用工具类调用那里
-
-
-
-
-
-
-
-
-
-
-
-
-
-
测试:
-
测试:
-
-
-
-
-
-
-
-
-
-
-
贰、再来玩一下C3P0 其实玩法都一样,就是jar包不一样,然后获取数据源哪里不一样而已
-
①、需要的两个jar包
- c3p0 我的是:c3p0-0.9.5.2
- mchange-commons-java 我的是:mchange-commons-java-0.2.15
-
①、需要的两个jar包
-
贰、再来玩一下C3P0 其实玩法都一样,就是jar包不一样,然后获取数据源哪里不一样而已
-
-
-
-
-
-
-
-
-
老规矩 —— 编写实体类、放jar包
-
老规矩 —— 编写实体类、放jar包
-
-
-
-
-
-
-
-
-
-
②、编写配置文件 C3P0使用的是xml配置
-
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config>
<!-- 数据库连接池 --> <named-config name="mysql"> <!-- 这个named-config名字很重要,后面获取数据源需要它 -->
<property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/blog</property> <property name="user">root</property> <property name="password">072413</property> <!--当数据连接池不够的时候,每次增长的个数--> <property name="acquireIncrement">5</property> <!--初始的连接池个数--> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">15</property> <!--后面这两个可要可不要--> <!--jdbc的标准参数 用于控制数据源里面的preparedStatement的数量--> <property name="maxStatements">0</property> <!--连接池内单个链接所拥有的最大的缓存statement数--> <property name="maxStatementsPerConnoection">5</property>
</named-config>
</c3p0-config>
-
-
②、编写配置文件 C3P0使用的是xml配置
-
-
-
-
-
-
-
-
-
-
③、编写工具类、获取数据源
-
package cn.xieGongZi.utils; import com.mchange.v2.c3p0.ComboPooledDataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JDBCUtil { // 这里需要一个东西 ComboPooledDataSource 后面要通过这个去获取数据源 private static ComboPooledDataSource dataSource = null; static { // 获取数据源 dataSource = new ComboPooledDataSource("mysql"); // 这里的参数名字就是前面配置文件中提到的named-config名字 } // 只需要改以上部分就完了 // 这里和DBCP一样的 // 封装获取数据库链接对象 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } // 释放资源方法还也是一样 // 提供释放资源的方法 public static void release(ResultSet rs , PreparedStatement ps , Connection con ) { // 注意:这里是用的PreparedStatement对象 if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( ps != null ){ try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( con != null ){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }结果就不展示了
-
-
③、编写工具类、获取数据源
-
-
-
-
至此:JDBC的相关知识完毕,还有深层次的封装,就是使用preparedStatement对象时
将 增删改 和 释放资源 及 查 都进行封装了( 注:完美的查可以使用两种方法:一是接口回调、二是反射 )
另外一个就是ThreadLocal这个小东西,这个没什么好说的,就是一个看源码就懂的东西
附:ThreadLocal就是为了处理事务的( ThreadLocal支持泛型 ),所以就是为了保证Connection对象是同一个
这个ThreadLocal的核心方法就三个:get()、set()、remove()
另外:
IDEA连接数据库( 即:集成过去 )在右上角Database中
这样集成过去之后,就不用把IDEA和数据库来回切换了
感兴趣的自行去摸索一下