第二天:JDBC和DBUtils
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二天:JDBC和DBUtils相关的知识,希望对你有一定的参考价值。
一、eclipse的使用
1、修改注释内容
选择window---->>preferences
选择Java---->>codestyle---->>code template---->>comments,然后双击types,修改里面的内容即可!
2、修改Eclipse里面的快捷键
举例:修改复制行内容快捷键。
1.点击window菜单->preferences子菜单->general->keys,进入快捷键管理界面
2.在这里可以查找所有功能的快捷键,需要修改或新增时,点击需要修改或新增的命令,在binding里设置快捷键
3.设置完快捷键后,还需要设置在什么时候可以使用该快捷键,eclipse提供各种场景供选择,一般选择In Windows(即在eclipse窗口激活状态)即可
完成以上操作,点击OK即完成设置
3、设置工作空间的编码
二、JUnit单元测试
1、介绍:JUnit是一个java语言的单元测试框架。属于第三方工具,一般情况下需要导入jar包,不过多数java开发环境已经集成了JUnit作为单元测试工具。
创建“day07”java项目,并创建“cn.itcast.a_junit”包
2、编写测试类,简单理解可以用于取代java的main方法
3、在测试类方法上添加注解@Test
4、注解修饰方法要求:public void 方法名(){...},方法名自定义建议test开头
5、添加STS中集成的Junit库,鼠标点击“@Test”,使用快捷键“ctrl+1”,点击“Add Junit...”
结果
6、使用:选中方法右键,执行当前方法;选中类名右键,执行类中所有方法(方法必须标注@Test)
7、常用注解
@Test,用于修饰需要执行的方法
@Before,测试方法前执行的方法
@After,测试方法后执行的方法
8、常见使用错误,如果没有添加“@Test”,使用“Junit Test”进行运行,将抛异常
三、JDBC
1、什么是JDBC
JDBC(Java DataBase Connectivity)就是java数据库连接,简单地说就是用java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句
<1>JDBC是一种用于执行SQL语句的java API
<2>JDBC可以为多种关系数据库提供统一访问入口
<3>JDBC由一组java工具类和接口组成
2、JDBC原理
早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC。而各个厂商提供的,遵循了JDBC规范的,可以访问自数据库的API被称之为驱动。
JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!
3、JDBC核心类(接口)介绍
JDBC中的核心类有:DriverManager、Connection、Statement和ResultSet
DriverManager(驱动管理器)的作用有两个:
<1>注册驱动:这可以让JDBC知道要使用的是哪个驱动;
<2>获取Connection:如果可以获取到Connection,那么说明已经与数据库连接上了。
Connection对象表示连接,与数据库的通讯都是通过这个对象展开的;
<3>Connection最为重要的一个方法就是用来获取Statement对象;
<4>Statement是用来向数据库发送SQL语句的,这样数据库会执行发送过来的的SQL语句
<5>void executeUpdate(String sql):执行更新操作(insert、update、delete等);
<6>ResultSet executeQuery(String sql):执行查询操作,数据库在执行查询后会返回查询结果,查询 结果就是ResultSer;
ResultSet对象表示查询结果集,只有在执行查询操作后才会有结果集的产生。结果集是一个人二维 的表格,有行有列。操作结果集要学习移动ResultSet内部的“行光标”,以及获取当前行上的数据
<7>boolean next():使“行光标”移动到下一行,并返回移动后的行是否存在;
<8>xxx getxxx(int col):获取当前行指定列上的值,参数就是列数,列数从1开始,而不是0
4、JDBC开发步骤
4.1 导入mysql数据库的驱动jar包:
<1>创建lib目录,用于存放当前项目需要的所有jar包
<2>选择jar包,右键执行build path/Add to Build Path
4.2 注册驱动
注册驱动就只有一句话:Class.forName("com.mysql.jdbc.Driver"),
下面的内容都是对这句代码的解释。今后我们的代码中,与注册驱动相关的代码只有这一句。
<1>分析步骤1:JDBC规范定义驱动接口:java.sql.Driver,
mysql驱动包提供了实现类:com.mysql.jdbc.Driver
<2>分析步骤2:DriverManager工具类,提供注册驱动的方法registerDriver(),
方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代码不推荐使用,存在两方面不足:
1.硬编码,后期不易于程序扩展和维护
2.驱动被注册两次
<3>分析步骤3:通常开发我们使用Class.forName()加载一个使用字符串描述的驱动类。
如果使用Class.forName()将加载到内存,该类的静态代码将自动执行
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
DriverManager类的registerDriver()方法的参数是java.sql.Driver,但java.sql.Driver是一个接口,实现类由mysql驱动来提供,mysql驱动中的java.sql.Driver接口的实现类为com.mysql.Driver那么注册驱动的代码如下:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
上面的代码虽然可以注册驱动,但是出现硬编码(代码依赖mysql驱动jar包),如果将来想连接Oracle数据库,那么必须要修改代码。并且其实这种注册驱动的方式是注册了两次驱动!
JDBC中规定,驱动类在被加载时,需要自己“主动”把自己注册到DriverManager中,下面来看看com.mysql.jdbc.Driver类的源代码:
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch(SQLException E) { throw newRuntimeException("Can‘t register driver!"); } } …… }
com.mysql.jdbc.Driver类中的static块会创建本类对象,并注册到DriverManager中,这说明只要去加载com.mysql.jdbc.Driver类,那么就会执行这个static块,从而也就会把com.mysql.jdbc.Driver注册到DriverManager中,所以可以把注册驱动类的代码修改为加载驱动类。
Class.forName("com.mysql.jdbc.Driver");
4.3 获取连接
代码:Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydbl","root","root")
获取连接需要两步,一是使用DriverManager来注册驱动,二是使用DriverManager来获取Connection对象。
获取连接的也只有一句话代码:
DriverManager.getConnection(url,username,password);
其中username和password是登录数据库的用户名和密码
url相对复杂一点,它是用来找到要连接数据库的“网址”,就好比你要在浏览器中查找百度时,也需要提供一个url。下面是mysql的url:
jdbc:mysql://localhost:3306/mydbl
JDBC规定url的格式由三部分组成,每个部分中间使用冒号分割。
<1>第一部分是jdbc,这是固定的;
<2>第二部分是数据库名称,那么连接mysql数据库,第二部分当然是mysql了;
<3>第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别 由数据库服务器的IP地址(localhost)、端口号(3306),以及database名称(mybdl)组成
下面是获取连接的语句:
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","root");
还可以在url中提供参数:
jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=UTF8
useUnicode参数指定这个连接数据库的过程中,使用的字节集是Unicode字节集;
characterEncoding参数指定java程序连接数据库的过程中,使用的字符集编码为UTF-8编码。注意,mysql中指定UTF-8编码给出的是UTF8,而不是UTF-8
4.4 获得语句执行
String sql="insert into category(cid,cname) values(‘c007‘,‘分类‘)";
Statement stmt=con.createStatement();
在得到Connection之后,说明已经与数据库连接上了,下面是通过Connection获取Statement对象的代码:
Statement stmt=con.createStatement();
Statement是用来向数据库发送要执行的SQL语句的
常用方法:
执行SQL语句:
<1>int executeUpdate(String sql);---执行insert update delete语句.(DML语句)
<2>ResultSet executeQuery(String sql);---执行select语句(DQL语句)
<3>boolean execute(String sql);---执行select返回true,执行其他的语句返回false
如果返回true,需要使用getResultSet()获得查询结果
如果返回false,需要使用getUpdateCount()获得影响行数
执行批处理:(可选)
addBatch(String sql);
clearBatch();
executeBatch();
4.5 预处理对象
使用处理对象时,建议每条sql语句所有的实际参数,都使用逗号分隔
String sql="insert into category(cid,cname) values(?,?)";
PreparedStatement psmt=con.prepareStatement(sql);
常用方法:
执行SQL语句:
<1>int executeUpdate(String sql);---执行insert update delete语句.(DML语句)
<2>ResultSet executeQuery(String sql);---执行select语句(DQL语句)
<3>boolean execute(String sql);---执行select返回true,执行其他的语句返回false
设置实际参数:
setxxx(int,T)通过setter方法将?占位符替换成实际参数
例如:setString()实际参数类型为hi字符串
执行批处理:(可选)
addBatch; 添加批处理的实际参数,需要调方法前执行对应setter方法
clearBatch();
executeBatch();
4.6 处理结果集
ResultSet就是一张二维的表格,它内部有一个“行光标”,光标默认位置在“第一行上方”,我们可以调用rs对象的next()方法把“行光标”向下移动一行,当第一次调用next()方法时,“行光标”就到了第一行记录的位置,这时就可以使用ResultSet提供的getxxx(int col)方法来获取指定列的数据了:
rs.next();//光标移动到第一行
rs.getInt(1);//光标移动到第一列的数据
当你使用rs.getInt(1)方法时,你必须可以肯定第1列的数据类型就是int类型,如果你不能肯定,那么最好使用rs.getObject(1)。在ResultSet类中提供了一系列的getxxx()方法,比较常用的方法有:
Object getObject(int col)
String getString(int col)
int getInt(int col)
double getDouble(int col)
4.7 释放资源
与IO流一样,使用后的东西都需要关闭,关闭的顺序是先得到的后关闭,后得到的先关闭
rs.close();
stmt.close();
con.close;
4.8 完成查询操作代码
public static Connection getConnection() throws Exception { Class.forName("com.mysql.jdbc.Driver"); String url ="jdbc:mysql://localhost:3306/web08"; return DriverManager.getConnection(url, "root", "root"); }
@Test public void query() throws Exception{ Connectioncon = getConnection(); Statementstmt = con.createStatement(); String sql ="select * from user"; ResultSet rs= stmt.executeQuery(sql); while(rs.next()){ String username = rs.getString(1); String password = rs.getString(2); System.out.println(username+ ", " + password); } }
4.9 规范化代码
所谓规范化代码就是无论是否出现异常,都要关闭ResultSet、Statement,以及Connection
@Test public void query() { Connection con = null; Statement stmt = null; ResultSet rs= null; try { con = getConnection(); stmt =con.createStatement(); String sql = "select * from user"; rs =stmt.executeQuery(sql); while(rs.next()){ String username = rs.getString(1); String password = rs.getString(2); System.out.println(username+ ", " + password); } } catch(Exceptione) { throw newRuntimeException(e); } finally { try { if(rs != null)rs.close(); if(stmt != null) stmt.close(); if(con != null)con.close(); } catch(SQLExceptione) {} } }
4.10 案例:查询所有
5、JDBC对象介绍
5.1JDBC中的主要类(接口)
<1>DriverManager
<2>Connection
<3>Statement
<4>ResultSet
5.2 DriverManager
我们今后只需要会用DriverManager的getConnection()方法即可:
<1>Class.forName("com.mysql.jdbc.Driver");
<2>String url="jdbc:mysql://localhost:3306/web08";
<3>String username="root"
<4>String password="root"
<5>Connection con=DriverManager.getConnection(url,username,password);
注意:上面代码可能出现的两种异常:
<1>ClassNotFoundException:这个异常是在第1句上出现的,出现这个异常有两个可能:
(1)你没有给出mysql的jar包;
(2)你把类名打错了,查看类名是不是com.mysql.jdbc.Driver。
<2>SQLException:这个异常出现在第5句,出现这个异常就是三个参数的问题,往往username和password一般不会出错,所以需要认真查看url是否打错
对于DriverManager.registerDriver()方法了解即可,因为我们今后注册驱动只会用 Class.forName(),而不会使用这个方法
5.3 Connection
Connection最为重要的方法就是获取Statement:
(1)Statement stmt=con.createStatement();
后面在学习ResultSet方法时,还要学习一下下面的方法
(2)Statement stmt=con.createStatement(int,int);
5.4 Statement
Statement最为重要的方法是:
<1>int executeUpdate(String sql):执行更新操作,即执行insert、update、delete语句,其实
这个方法也可以执行create table、alter table,以及drop table等语句,但我们很少会使JDBC
来执行这些语句;
<2>ResultSet executeQuery(String sql):执行查询语句,执行查询操作会返回ResultSet,即结果集;
<3>boolean execute():Statement还有一个boolean execute()方法,这个方法可以用来执行 增、删、改、查所有的SQL语句,该方法返回的是boolean类型,表示SQL语句是否执行成功。
如果使用execute()方法执行的是更新语句,那么还要调用int getUpdateCount()来获取insert、 update、delete语句所影响的行数
如果使用execute()方法执行的是查询语句,那么还要调用ResultSet、getResultSet()来获取 select语句的查询结果
5.5 ResultSet之滚动结果集(了解)
ResultSet表示结果集,它是一个二维的表格!ResultSet内部维护一个行光标(游标),ResultSet提供了一系列的方法来移动游标:
<1>void beforeFirst():把光标放到第一行的前面,这也是光标默认的位置;
<2>void afterLast():把光标放到最后一行的后面;
<3>boolean first():把光标放到第一行的位置上,返回值表示调控光标是否成功;
<4>boolean last():把光标放到最后一行的位置上;返回值表示调控光标是否成功
<5>boolean isBeforeFirst():当前光标位置是否在第一行前面;
<6>boolean isAfterLast():当前光标位置是否在最后一行的后面;
<7>boolean isFirst():当前光标位置是否在第一行上;
<8>boolean isLast:当前光标位置是否在最后一行上;
<9>boolean next():把光标向下挪一行;
<10>boolean relative(int row):相对位移,当row为正数时,表示向下移动row行,为负数时 表示向上移动row行;
<11>boolean absolute(int row):绝对位移,把光标移动到指定行上;
<12>int getRow():返回当前光标所有行
上面方法分为两类,一类用来判断游标位置的,另一类是用来移动游标的。如果结果集是不可滚动的,那么只能使用next()方法来移动游标。而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用!
结果集是否支持滚动,要从Connection类的createStatement()方法说起,也就是说创建的Statement决定了使用Statement创建的ResultSet是否支持滚动
Statement createStatement(int resultSetType,int resultSetConcurrency)
resultSetType的可选值:
(1)ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
(2)ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变 化;
(3)ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,但结果集数据不会在跟随数据库而变化
可以看出,如果想使用滚动的结果集,我们应该选择TYPE_SCROLL_INSENSITIVE!其实很少有数据库驱动会支持TYPE_SCROLL_SENSITIVE的特性!通常我们也不需要查询到的结果集再受到数据库变化的影响
resultSetConcurrency的可选值:
(1)CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库;
(2)CONCUR_UPDATABLE:结果集是可更新的,对结果集的更新可以反向影响数据库;
通常可更新结果集这一“高级特性”我们也是不需要的!
获取滚动结果集的代码如下:
Connection con=Statement stmt=con.createStatement(ResultSet.TYPE.SCROLL.INSENSITIVE,CONCUR_READ_ONLY);
String sql=....//查询语句
ResultSet rs=stmt.executeQuery(sql);//这个结果集是可滚动的
5.6 ResultSet之获取列数据
可以通过next()方法使ResultSet的游标向下移动,当游标移动到你需要的行时,就需要来获取该行的数据了,ResultSet提供了一系列的获取列数据的方法:
(1)String getString(int columnIndex):获取指定列的String类型数据;
(2)int getInt(int columnIndex):获取指定列的int类型数据
(3)double getDouble(int columnIndex):获取指定列的double类型数据;
(4)boolean getBoolean(int columnIndex):获取指定列的boolean类型数据
(5)Object getObject(int columnIndex):获取指定列的Object类型的数据
上面的方法中,参数columnIndex表示列的索引,列索引从1开始,而不是0,这第一点与数组不同,如果你清楚当前列的数据类型,那么可以使用getInt()之类的方法来获取,如果你不清楚列的类型,那么你应该使用getObject()方法类获取
ResultSet还提供了一套通过列名称来获取列数据的方法:
(1)String getSting(String columnName):获取名称为columnName的列的String数据;
(2)int getInt(String columnName):获取名称为columnName的列的int数据
(3)double getDouble(String columnName):获取名称为columnName的列的double数据;
(4)boolean getBoolean(String columnName):获取名称为columnName的列的boolean数据;
(5)Object getObject(String columnName):获取名称为columnName的列的Object数据
6、SQL注入
6.1 什么是SQL攻击
在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们dao中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段
6.2 演示SQL攻击
首先我们需要创建一张用户表,用来存储用户的信息
CREATE TABLE user( uid CHAR(32)PRIMARY KEY, username VARCHAR(30)UNIQUE KEY NOT NULL, PASSWORD VARCHAR(30) ); INSERT INTO userVALUES(‘U_1001‘, ‘zs‘, ‘zs‘); SELECT * FROM user;
现在用户表中只有一行记录,就是zs。
下面我们写一个login()方法
public void login(String username, String password) { Connectioncon = null; Statementstmt = null; ResultSet rs= null; try { con =JdbcUtils.getConnection(); stmt =con.createStatement(); String sql = "SELECT * FROM user WHERE " + "username=‘" +username + "‘and password=‘" + password + "‘"; rs =stmt.executeQuery(sql); if(rs.next()){ System.out.println("欢迎" +rs.getString("username")); } else { System.out.println("用户名或密码错误!"); } } catch(Exception e) { throw newRuntimeException(e); } finally { JdbcUtils.close(con,stmt, rs); } }
下面是调用这个方法的代码:
login("a‘ or ‘a‘=‘a", "a‘or ‘a‘=‘a");
这行当前会使我们登录成功,因为输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起,我们来看看组合在一起的SQL语句
SELECT * FROM tab_user WHERE username=‘a‘or ‘a‘=‘a‘ and password=‘a‘ or ‘a‘=‘a‘
6.3防止SQL攻击
过滤用户输入的数据中是否包含非法字符;
分步校验,先使用用户名来查询用户,如果查找到了,再比较密码;
使用PreparedStatement
6.4 PreparedStatement是什么?
PreparedStatement叫预编译声明
PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement
PreparedStatement的好处:
(1)防止SQL攻击
(2)提高代码的可读性,以及可维护性
(3)提高效率
6.5 PreparedStatement的使用
使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;
调用PreparedStatement的setXXX()系列方法为问号设置值;
调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
String sql = “select * from tab_studentwhere s_number=?”; PreparedStatement pstmt =con.prepareStatement(sql); pstmt.setString(1, “S_1001”); ResultSet rs = pstmt.executeQuery(); rs.close(); pstmt.clearParameters(); pstmt.setString(1, “S_1002”); rs= pstmt.executeQuery();
在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数
在得到PreparedStatement对象后,调用它的setxxx()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象
注意:PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。
PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因
所依,在以后的开发中,无论什么情况,都去使用PreparedStatement,而不是使用Statement.
7、使用JDBC完成分类表CRUD(增删改查)的操作
7.1 案例分析
使用JDBC对分类表category进行增删改查操作
7.2 工具类
“获得连接”和“释放资源”两次代码将在之后的增删改查所有功能中都存在,开发中一般遇到此种情况,将采用工具类的方法进行抽取,从而达到代码的重复利用
7.3 获得连接
7.4 释放资源
如果释放资源采用依次关闭三个对象,那么第一个对象关闭时抛出了异常,后面两个对象将无法成功释放资源,通常我们使用try-catch块进行处理
方法1:多个try-catch块,将资源释放,容易理解
方法2:try-catch-finally嵌套,资源释放时如果出错,将通知调用者
7.5 使用properties配置文件
开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可
通常情况下,我们习惯使用properties文件,此文件我们将做如下要求:
<1>文件位置:任意,建议src下
<2>文件名称:任意,扩展名为properties
<3>文件内容:一行一组数据,格式是“key=value”
(1)key命名自定义,如果是多个单词,习惯使用点分割。例如:jdbc.driver
(2)value值不支持中文,如果需要使用非英文字符,将进行unicode转换
7.5.1 创建配置文件
右键/New/File,输入“db.properties”文件名
7.5.2 加载配置文件:ResourceBundle对象
ResourceBundle提供getBundle()方法用于只提供properties文件即可,之后使用getString(key)通过key获得value的值
7.5.3 获得连接
7.5.4 加载配置文件:Properties对象(可选)
对应properties文件处理,开发中也会使用Properties对象进行。在v3版本中我们将采用加载properties文件获得流,然后使用Properties对象进行处理
7.6 实现
7.6.1 模板
7.6.2 添加:insert into
7.6.3 更新:update...set
7.6.4 删除:delete
7.6.5 通过ID查询
四、JDBC连接池
1、案例分析
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection
2、连接池概述
2.1 概念
用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection"归还"给池。池就可以再利用这个Connection对象了。
2.2 规范
java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序才可以方便的切换不同厂商的连接池!
常见的连接池:DBCP、C3P0
3、自定义连接池
3.1 案例分析
我们编写自定义连接池,需要完成一下步骤
3.1.1 创建连接池实现(数据源),并实现接口javax.sql.DataSource。因为我们只使用该接口中getConnection()方法,简化本案例,我们将自己提供方法1,而没有实现接口
3.1.2 提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList
3.1.3 本案例在静态代码块中,为连接池初始化3个连接
3.1.4 之后程序如果需要连接,调用实现类的getConnection(),本方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们将连接先从连接池中移除
3.1.5 当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中
3.2 案例实现
3.2.1 提供容器及初始化
3.2.2 获得连接
3.2.3 归还连接
3.2.4 测试使用
为了体现连接池优势,我们将采用多线程并发访问,使同一个连接在不同的时段,被不同的线程使用
3.3 自定义连接池:方法增强
3.3.1 需求
自定义连接池中存在严重问题,用户调用getConnection()获得连接后,必须使用release()方法进行连接的归还,如果用户调用conn.close()将连接真正的释放,连接池中将出现无连接可用。
此时我们希望,即使用户调用了close()方法,连接仍归还给连接池。close()方法原有功能释放资源,期望功能:将当前连接归还连接池。说明close()方法没有我们希望的功能,我们将对close()方法进行增强,从而实现将连接归还给连接池的功能
3.3.2 方法增强总结
<1>继承,子类继承父类,将父类的方法进行腹复写,从而进行增强
使用前提:必须有父类,且存在继承关系
<2>装饰者设计模式,此设计模式专门用于增强方法
使用前提:必须有接口
缺点:需要将接口的所有方法都实现
<3>动态代理:在运行时动态的创建代理类,完成增强操作。与装饰者相似
使用前提:必须有接口
难点:需要反射技术
<4>字节码增强,运行时创建目标类子类,从而进行增强
常见第三方框架:cglib、javassist等
3.3.3 装饰者设计模式
设计模式:专门为解决一类问题,而编写的固定格式的代码。
装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B
<1>创建类B,并实现接口A
<2>提供类B的构造方法,参数类型为A,用于接收A接口的其他实现类(C)
<3>给类B添加类型为A成员变量,用于存放A接口的其他实现类
<4>增强需要的方法
<5>实现不需要增强的方法,方法体重调用成员变量存放的其他实现类对应的方法
3.3.4 实现
3.3.4.1 装饰类
3.3.4.2 使用装饰者(包装类)
将由DriverManager创建的连接,使用装饰类包装一下,然后添加到连接池中,构造方法中将容器pool传递进去,方便连接的归还
3.3.4.3 使用连接
4、C3P0连接池
C3P0是开源免费的连接池,目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,C3P0使用时还需要添加配置文件c3p0-config.xml
4.1 导入jar包
我们使用的0.9.2版本,需要导入2个jar包
4.2 配置文件
<1>配置文件名称:c3p0-config.xml(固定)
<2>配置文件位置:src(类路径)
<3>配置文件内容:命名配置
配置文件内容:默认配置
4.3 常见配置项
4.4 编写工具类
C3P0提供核心工具类:ComboPooledDataSource,如果要使用连接池,必须创建该类的实例对象。
5、DBCP连接池
DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池
5.1 导入jar包
5.2 配置文件
<1>配置文件名称:*.properties
<2>配置文件位置:任意,建议src(classpath/类路径)
<3>配置文件内容:properties不能编写中文,不支持在STS中修改,必须使用记事本修改内容,否则中文注释就乱码了
5.3 常见配置项
5.4 编写工具类
五、使用DBUtils增删改查的操作
1、案例分析
如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,我们将采用apache commons组件一个成员:DEUtils。
DBUtils就是JDBC的简化开发工具包。需要使用技术:连接池(获得连接),SQL语句都没有少
2、案例相关知识
2.1、JavaBean组件
javaBean就是一个类,在开发中常用于封装数据。具有如下特性
<1>需要实现接口:java.io.Serializable,通常偷懒省略了。
<2>提供私有字段:private类型 字段名;
<3>提供getter/setter方法
<4>提供无参构造
3、DBUtils完成CRUD
3.1 概述
DBUtils是java编程中的数据库操作实用工具,小巧简单实用
DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码
DBUtils三个核心功能介绍
<1>QueryRunner中提供对sql语句操作的API
<2>ResultSetHandler接口,用于定义select操作后,怎样封装结果集
<3>DBUtils类,它就是一工具类,定义了关闭资源与事务处理的方法
3.2 QueryRunner核心类
<1>QueryRunner(DataSource ds),提供数据源(连接池),DBUtils底层自动维护连接connection
<2>update(String sql,Object...params),执行更新数据
<3>query(String sql,ResultSetHandler<T>rsh,Object...params),执行查询
3.3 ResultSetHandler结果集处理类
3.4 DBUtils工具类
closeQuietly(Connection conn) 关闭连接,如果有异常,try后不抛
commitAndCloseQuietly(Connection conn) 提交并关闭连接
rollbackAndCloseQuietly(Connection conn) 回滚并关闭连接
3.5 实现
3.5.1 添加
3.5.2 更新
3.5.3 删除
3.5.4 通过id查询
3.5.5 查询所有
3.5.6 总记录数
本文出自 “鹏哥的博客” 博客,谢绝转载!
以上是关于第二天:JDBC和DBUtils的主要内容,如果未能解决你的问题,请参考以下文章