jdbc(Java数据库连接)是一种用于执行SQL语句的Java API,由一组用Java语言编写的类和接口组成。
驱动:两个设备(应用)之间通信的桥梁。
SUN公司提供一套统一的规范(接口)。然后各个数据库生产商提供这套接口的实现。这套接口规范就是JDBC的规范。
JDBC的环境准备:
\1. 创建数据库和表
\2. 创建项目,引入jar包
JDBC的开发步骤:
1.加载驱动:Class.forname
2.获得连接:DriverManager
\3. 基本操作:执行SQL:
获得执行SQL语句的对象:createStatement()方法,返回值是Statement
编写SQL语句:String sql = “select * from user”;
执行SQL:executeQuery(sql)方法,返回值是ResultSet
遍历该set集合
\4. 释放资源:集合、执行SQL语句的对象、获得的连接都要释放资源
DriverManager:驱动管理类
作用:1.注册驱动:DriverManager.registerDriver(Driver driver);
这个方法可以完成驱动的注册,但是实际开发中一般不会使用这个方法完成驱动的注册,而是使用Class.forname(“com.mysql.jdbc.Driver”);
因为如果需要注册驱动,就会使用DriverManager.registerDriver(new Driver());,但是查看Driver源代码发现,在代码中有一段静态代码块,静态代码块已经调用了注册驱动的方法,如果再手动调用该方法注册驱动,就会导致驱动被注册两次
2.获得连接:DriverManager.getConnection(String url, String username, String password);
url:与数据库连接的路径
user :与数据库连接的用户名
password:与数据库连接的密码
url的写法:jdbc:mysql://localhost:3306/web_test3
jdbc :连接数据库的协议
mysql:是jdbc的子协议
localhost:连接的MySQL数据库服务器的主机地址。(连接是本机就可以写成localhost),如果连接不是本机的,就需要写上连接主机的IP地址。
3306:MySQL数据库服务器的端口号
web_test3:数据库名称
url如果连接的是本机的路径,可以简化为:jdbc:mysql:///web_test3
Connection:与数据库连接对象
作用:1.创建执行SQL语句的对象:
Statement:执行SQL Statement createStatement()
CallableStatement:执行数据库中存储过程 CallableStatement prepareCall(String sql)
PreparedStatement:(主要用这个)执行SQL.对SQL进行预处理。解决SQL注入漏洞 PreparedStatement preparedStatement (String sql)
\2. 管理事务
void setAutoCommit(boolean autoCommit):将此连接的自动提交模式改为给定状态
void commit():将所有上一次提交/回滚后进行的更改变为永久性更改,并释放此Connection对象当前持有的所有数据库锁
void rollback():取消在当前事务中进行的所有更改,并释放此Connection对象当前持有的所有数据库锁
Statement:执行SQL
作用:1.执行SQL
boolean execute(String sql); 执行查询,修改,添加,删除的SQL语句 (如果第一个结果为ResultSet对象,则返回true;如果是更新计数或者不存在任何结果,返回false)
ResultSet executeQuery(String sql); 执行查询(执行select语句)
int executeUpate(String sql);执行修改,添加,删除的SQL语句 (SQL DDL语句也可以)(返回的是影响的行数)
2.执行批处理
void addBatch(String sql) 将给定的sql命令添加到此Statement对象的当前命令列表中
void clearBatch() 清空此Statement对象的当前SQL命令列表
int[] executeBatch() 将一批命令交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组
ResultSet:结果集,通过select语句的查询结果(只有select语句才有结果集)
结果集的遍历:
boolean next(); 将光标从当前位置向下移一行(刚开始是在第一行之前的空白,调用了该方法之后会移动到第一行)
结果集的获取:
结果集获取可以使用结果集中的: getXXX(); 方法通常都会有一个重载的方法(int、long、String,Object就比较通用)
传递的参数可以是列号也可以是列名:
getXXX(int columnIndex);
getXXX(String columnName); (一般传的参数是列名,因为查询的话,有可能结果的列号不一样)
资源释放:
JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection
这几个对象中尤其是Connection对象是非常稀有的。这个对象一定要做到尽量晚创建,尽早释放掉。
将资源释放的代码写入到finally的代码块中,确保资源会释放。但是资源释放的时候仍可能出现异常,所以资源释放的代码应该写的标准:再用trycatch语句进行判空,判完之后要将对象赋值为null:因为可以让垃圾回收器尽快的将资源回收。
JDBC的CRUD操作:
添加、删除、修改操作:
\1. 创建Connection和Statement并赋值为null
\2. try语句中进行:注册驱动,获得连接,创建执行SQL语句对象,编写相应的SQL语句,执行SQL(excuteUpdate(sql)方法),对返回值进行判断。
\3. 释放资源(判空,抛异常,赋值为null)
查询操作:查询多条记录和查询一条记录
1.创建Connection和Statement和ResultSet并赋值为null
2.try语句中进行:注册驱动,获得连接,创建执行SQL语句对象,编写相应的SQL语句,执行SQL(excuteQuery(sql)方法)。多条语句进行遍历while,一条语句进行判断if,并输出
3.释放资源(判空,抛异常,赋值为null)
JDBC的工具类的抽取:因为注册驱动,获得连接,释放资源的代码都是相同的,所以可以抽取成一个工具类。
/**
* JDBC的工具类
* *@author* swx
*/
*public* *class* JDBCUtils {
*private* *static* *final* String ****driverClassName****;
*private* *static* *final* String ****url****;
*private* *static* *final* String ****username****;
*private* *static* *final* String ****password****;
*static*{
****driverClassName****=”com.mysql.jdbc.Driver”;
****url****=”jdbc:mysql:///web_test3”;
****username****=”root”;
****password****=”abc”;
}
/**
* 注册驱动的方法
*/
*public* *static* *void* loadDriver(){
*try* {
Class.forName(****driverClassName****);
} *catch* (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获得连接的方法
*/
*public* *static* Connection getConnection(){
Connection conn = *null*;
*try*{
// 将驱动一并注册:
loadDriver();
// 获得连接
conn = DriverManager.getConnection(*url****,*username****, ****password****);
}*catch*(Exception e){
e.printStackTrace();
}
*return* conn;
}
/**
* 释放资源的方法
*/
*public* *static* *void* release(Statement stmt,Connection conn){
*if*(stmt != *null*){
*try* {
stmt.close();
} *catch* (SQLException e) {
e.printStackTrace();
}
stmt = *null*;
}
*if*(conn != *null*){
*try* {
conn.close();
} *catch* (SQLException e) {
e.printStackTrace();
}
conn = *null*;
}
}
*public* *static* *void* release(ResultSet rs,Statement stmt,Connection conn){
// 资源释放:
*if*(rs != *null*){
*try* {
rs.close();
} *catch* (SQLException e) {
e.printStackTrace();
}
rs = *null*;
}
*if*(stmt != *null*){
*try* {
stmt.close();
} *catch* (SQLException e) {
e.printStackTrace();
}
stmt = *null*;
}
*if*(conn != *null*){
*try* {
conn.close();
} *catch* (SQLException e) {
e.printStackTrace();
}
conn = *null*;
}
}
}
静态代码块可以写成配置文件
配置文件:两种
\1. 属性文件:扩展名为.properties 内容是key=value
\2. XML文件
定义一个配置文件:写好键值对的相应配置,修改文件扩展名为.properties
在工具类中解析属性文件:依然在静态代码块中进行解析:
SQL注入漏洞:通过知道用户名,然后进行sql语句查询就可以进行登录。
“+username+”和“+password+” 是字符串拼接
当用户名输入为aaa’ or ‘1=1 ,密码随意写,就能登录成功:
select * from user where username = ‘ aaa’ or ‘ 1=1 ‘ and password = ‘ asdafaf ’
因为先进行and运算,true and false 结果为false 。再进行or运算, true or false 结果为true,所以可以成功。
当用户名输入为aaa’ or ‘1=1 ,密码随意写,也能登录成功:
select * from user where username = ‘ aaa’ – ‘ and password = ‘ asdafaf ’
因为– 代表注释,相当于aaa后面的都没了,结果为true,所以可以成功。
出现漏洞的原因就是因为 or 和 – 都是SQL的关键字
解决方案:得在后台,后端解决。(因为可以绕过前端)
需要采用PreparedStatement对象解决SQL注入漏洞。这个对象将SQL预先进行编译,使用?作为占位符。?所代表内容是SQL所固定。再次传入变量(包含SQL的关键字)。这个时候也不会识别这些关键字。
*public* *class* UserDao {
*public* *boolean* login(String username,String password){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
ResultSet rs = *null*;
// 定义一个变量:
*boolean* flag = *false*;
*try*{
// 获得连接:
conn = JDBCUtils.getConnection();
// 编写SQL语句:
String sql = “select * from user where username = ? and password = ?”;
// 预编译SQL
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, username); //1代表第一个问号
pstmt.setString(2, password); // 2代表第二个问号
// 执行SQL语句:
rs = pstmt.executeQuery();
*if*(rs.next()){
// 说明根据用户名和密码可以查询到这条记录
flag = *true*;
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(rs, pstmt, conn);//这里传的pstmt实际是stmt的子类,实际上用的是多态
}
*return* flag;
}
所以以后的Statement都最好用PreparedStatement
增加操作:String sql = “insert into user values (null,?,?,?,?)”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, “eee”);
pstmt.setString(2, “abc”);
pstmt.setString(3, “旺财”);
pstmt.setInt(4, 32);
// 执行SQL
*int* num = pstmt.executeUpdate();
*if*(num > 0){
System.****out****.println(“保存成功!”);
}
修改操作:String sql = “update user set username = ?,password =?,nickname=?,age = ? where id = ?”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, “abc”);
pstmt.setString(2, “1234”);
pstmt.setString(3, “旺旺”);
pstmt.setInt(4, 23);
pstmt.setInt(5, 6);
// 执行SQL:
*int* num = pstmt.executeUpdate();
*if*(num > 0){
System.****out****.println(“修改成功!”);
}
删除操作:String sql = “delete from user where id = ?”;
// 预编译SQL
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setInt(1, 4);
// 执行SQL:
*int* num = pstmt.executeUpdate();
*if*(num > 0){
System.****out****.println(“删除成功!”);
}
查找操作:String sql = “select * from user”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 执行SQL:
rs = pstmt.executeQuery();
// 遍历结果集:
while(rs.next()){
System.out.println(rs.getInt(“id”)+” “+rs.getString(“username”)+” “+rs.getString(“password”)+” “+rs.getString(“nickname”));
}
JDBC的批处理:之前进行JDBC的操作的时候,都是一条SQL语句执行。现在如果使用批处理,可以将一批SQL一起执行。
用Statement进行批处理:@Test
/**
* 批处理基本操作
*/
*public* *void* demo1(){
Connection conn = *null*;
Statement stmt = *null*;
*try*{
// 获得连接:
conn = JDBCUtils.getConnection();
// 创建执行批处理对象:
stmt = conn.createStatement();
// 编写一批SQL语句:
String sql1 = “create database test1”;
String sql2 = “use test1”;
String sql3 = “create table user(id int primary key auto_increment,name varchar(20))”;
String sql4 = “insert into user values (null,’aaa’)”;
String sql5 = “insert into user values (null,’bbb’)”;
String sql6 = “insert into user values (null,’ccc’)”;
String sql7 = “update user set name = ‘mmm’ where id = 2”;
String sql8 = “delete from user where id = 1”;
// 添加到批处理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
stmt.addBatch(sql7);
stmt.addBatch(sql8);
// 执行批处理:
stmt.executeBatch();
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(stmt, conn);
}
}
使用PreparedStatement进行批量插入:
@Test
/**
* 批量插入记录:
* * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可:
?rewriteBatchedStatements=true
*/
*public* *void* demo2(){
// 记录开始时间:
*long* begin = System.currentTimeMillis();
Connection conn = *null*;
PreparedStatement pstmt = *null*;
*try*{
// 获得连接:
conn = JDBCUtils.getConnection();
// 编写SQL语句:
String sql = “insert into user values (null,?)”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
*for*(*int* i=1;i<=10000;i++){
pstmt.setString(1, “name”+i);
// 添加到批处理
pstmt.addBatch();
// 注意问题:
// 执行批处理:每1000次进行一次批量处理和清空,以防止内存溢出
*if*(i % 1000 == 0){
// 执行批处理:
pstmt.executeBatch();
// 清空批处理:
pstmt.clearBatch();
}
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(pstmt, conn);
}
*long* end = System.currentTimeMillis();
System.****out****.println((end-begin));
}
JDBC的事务:
事务指的是逻辑上的一组操作,组成这组操作各个逻辑单元要么全都成功,要么全都失败。
如果写一个转账代码,没有加事务的话,可能会出现扣钱了,但是另一个账户没有加钱的情况,所以需要加事务。
@Test
/**
* 完成转账的案例
*/
*public* *void* demo1(){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
*try*{
/**
* 完成转账代码:
* * 扣除某个账号的钱
* * 给另外一个账号加钱
*/
// 获得连接:
conn = JDBCUtils.getConnection();
*// 开启事务*
*conn.setAutoCommit(*****false**);**//设置为false是需要手动提交,设为true的话,写一条sql语句就会提交一句****
// 编写SQL语句:
String sql = “update account set money = money + ? where name = ?”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 用aaa账号给bbb账号转1000元
pstmt.setDouble(1, -1000);
pstmt.setString(2, “aaa”);
// 执行SQL:扣除aaa账号1000元
pstmt.executeUpdate();
*int* i = 1 / 0; //假设这里出现了异常,结果账户的钱也不会发生改变
// 给bbb账号加1000
pstmt.setDouble(1, 1000);
pstmt.setString(2, “bbb”);
pstmt.executeUpdate();
*// 提交事务:*
*conn.commit();*
}*catch*(Exception e){
*// 回滚事务:*
*try* *{*
*conn.rollback();*
*}* *catch* *(SQLException e1) {*
*e1.printStackTrace();*
*}*
e.printStackTrace();
}*finally*{
JDBCUtils.release(pstmt, conn);
}
}
*连接池*
连接池是装有连接的容器,线程使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。
连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)
自定义连接池:
\1. 编写一个类实现DataSource接口
\2. 重写getConnection方法
\3. 初始化多个连接在内存中
\4. 编写归还连接的方法
测试:
但是这个自定义连接池也存在两个问题:
使用接口的实现类完成的构造:
MyDataSource dataSource = new MyDataSource(); 这种写法不利于程序的扩展:
应该用这样的DataSource dataSource = new MyDataSource(); (多态)
但是这样的话,因为DataSource下没有addBack方法所以下面的调用会出错;同时因为调用了addBack方法,会对使用连接池的用户增加难度(因为要理解什么是addBack方法,额外提供了方法归还连接)
解决:不提供自定义的方法addBack就可以解决这个问题,所以需要改写一个方法解决归还的问题。
在Connection中是有一个close方法的,colse方法完成了连接的销毁。现在需要写一个方法,将原有的连接的close方法改为归还。
增强一个类中的方法:
\1. 采用继承:是最简单的一种增强某个类中的方法的方式。
条件:需要控制这个类的构造才能进行继承。
\2. 采用装饰者模式:比如:BufferedInputStream(InputStream)
条件:增强的类和被增强的那个类要实现同样的接口;在增强的类中要有被增强的那个类的引用。
\3. 动态代理
使用装饰者模式增强Connection中的close方法:(因为接口内的方法需要全部重写,而继承的话,可以只重写某一个或几个方法)
为了简化编程,提供一个模板类(模板类原封不动的将接口中的所有方法都实现,但是都没有增强)。编写一个装饰类继承模板类。在装饰类中只需要增强某一个方法即可。
连接池的代码:
*public* *class* MyDataSource *implements* DataSource {
// 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
*private* List
// 在初始化的时候提供一些连接
*public* MyDataSource() {
// 初始化连接:
*for*(*int* i = 1;i<=3;i++){
// 向集合中存入连接:
connList.add(JDBCUtils.getConnection());
}
}
// 从连接池中获得连接的方法
@Override
*public* Connection getConnection() *throws* SQLException {
Connection conn = connList.remove(0);
*// 增强连接:*
*MyConnectionWrapper connWrapper =* *new* *MyConnectionWrapper(conn, connList);*
*return* connWrapper;
}
*// 编写一个归还连接的方法:*
*/*public void addBack(Connection* *conn*****){****
*connList.add(*****conn*****);*
*}*/*
}
测试:
*Druid开源连接池*
Druid是阿里旗下开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。
在项目下新建一个文件夹folder名为lib,复制mysql的驱动包,druid的jar包到lib中,添加buid path。
/可以直接添加工具类,配置文件。然后直接获得连接,执行SQL语句,释放资源。(但是最好用连接池,不用这个)/
使用这个druid连接池可以去百度搜索它的帮助文档查看相关的API。核心类:DruidDataSource
利用这个核心类可以创建对象,然后手动设置数据库连接的参数:setXxx的方法,(也可以使用配置文件进行获取参数)获得连接:getConnection方法。最后释放资源中conn的释放不是销毁而是归还到连接池中(Druid连接池底层一定重写了这个方法变成了归还)
@Test
/**
* Druid的使用:
* * 手动设置参数的方式
*/
*public* *void* demo1(){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
ResultSet rs = *null*;
*try*{
// 使用连接池:
DruidDataSource dataSource = *new* DruidDataSource();
// 手动设置数据库连接的参数:
dataSource.setDriverClassName(“com.mysql.jdbc.Driver”);
dataSource.setUrl(“jdbc:mysql:///web_test4”);
dataSource.setUsername(“root”);
dataSource.setPassword(“abc”);
// 获得连接:
// conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
// 编写SQL:
String sql = “select * from account”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 执行SQL:
rs = pstmt.executeQuery();
*while*(rs.next()){
System.****out****.println(rs.getInt(“id”)+” “+rs.getString(“name”)+” “+rs.getDouble(“money”));
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(rs, pstmt, conn);
}
}
也可以用配置文件进行参数的获取:
@Test
/**
* Druid的使用:
* * 配置文件方式设置参数
* Druid配置方式可以使用属性文件配置的。
* * 文件名称没有规定但是属性文件中的key要一定的。
*/
*public* *void* demo2(){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
ResultSet rs = *null*;
*try*{
// 使用连接池:
// 从属性文件中获取:
Properties properties = *new* Properties();
properties.load(*new* FileInputStream(“src/druid.properties”));
//这里()内需要传一个InputStream,现在可以这样子传,在WEB中是另一种。
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获得连接:
// conn = JDBCUtils.getConnection();
conn = dataSource.getConnection();
// 编写SQL:
String sql = “select * from account”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
// 执行SQL:
rs = pstmt.executeQuery();
*while*(rs.next()){
System.****out****.println(rs.getInt(“id”)+” “+rs.getString(“name”)+” “+rs.getDouble(“money”));
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(rs, pstmt, conn);
}
}
虽然可以在这里进行配置文件的配置,但是不太好,可以放在JDBC的工具类中。
*C3P0开源连接池*
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
C3P0的核心类是:ComboPooledDataSource
手动设置参数:
@Test
/**
* 手动设置参数的方式:
*/
*public* *void* demo1(){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
ResultSet rs = *null*;
*try*{
// 获得连接:从连接池中获取:
// 创建连接池:
ComboPooledDataSource dataSource = *new* ComboPooledDataSource();
// 设置连接参数:
dataSource.setDriverClass(“com.mysql.jdbc.Driver”);
dataSource.setJdbcUrl(“jdbc:mysql:///web_test4”);
dataSource.setUser(“root”);
dataSource.setPassword(“abc”);
// 从连接池中获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = “select * from account”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 执行SQL:
rs = pstmt.executeQuery();
*while*(rs.next()){
System.****out****.println(rs.getInt(“id”)+” “+rs.getString(“name”)+” “+rs.getDouble(“money”));
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(rs, pstmt, conn);
}
}
配置文件设置参数:
默认情况下,c3p0会在类加载的resource路径下查找一个配置文件“/c3p0-config.xml”。所以XML文件应该在应用的CLASSPATH下的一个文件夹或者jar包中,如果是WEB的话会在WEB-INF/classes下,其他的话也是类似的路径。c3p0不仅支持xml文件,也支持properties文件(以后的重点)。
配置连接池:
在src下新建一个xml文件,设置名为c3p0-config.xml,可以在帮助文档中找到xml的配置代码:
<named-config name=” ”> …… 自定义配置
可以修改其中的配置key,value。
使用连接池:
@Test
/**
* 采用配置文件的方式:
*/
*public* *void* demo2(){
Connection conn = *null*;
PreparedStatement pstmt = *null*;
ResultSet rs = *null*;
*try*{
// 获得连接:从连接池中获取:
// 创建连接池://创建连接池默认去类路径下查找c3p0-config.xml,并且将其中的各种参数加载好
ComboPooledDataSource dataSource = *new* ComboPooledDataSource();
//()里面可以传递配置的名称从而加载不同的参数。如果名称没有匹配的,会加载默认的配置参数
// 从连接池中获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = “select * from account”;
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 执行SQL:
rs = pstmt.executeQuery();
*while*(rs.next()){
System.****out****.println(rs.getInt(“id”)+” “+rs.getString(“name”)+” “+rs.getDouble(“money”));
}
}*catch*(Exception e){
e.printStackTrace();
}*finally*{
JDBCUtils.release(rs, pstmt, conn);
}
}
参数可以设置很多个,很多种,所以其应用很灵活。
以上的代码都不规范,因为每次都会创建一个新的连接池,会浪费资源,所以需要对工具类进行改写和完善
*DBUtils*
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
因为JDBC手写比较麻烦,而且有非常多的代码是类似的。比如获得连接,预编译SQL,释放资源等..那么可以将这些代码抽取出来放到工具类中。将类似的代码进行抽取。大大简化JDBC的编程。
API
QueryRunner对象:核心运行类
构造方法:
无参构造
带参构造
方法:
增删改的方法
查找的方法
在一般情况下如果执行CRUD的操作:
构造:
QueryRunner(DataSource ds);
方法:
int update(String sql,Object… args);
T query(String sql,ResultSetHandler rsh,Object… args);
如果有事务管理的话使用另一套完成CRUD的操作
构造:
QueryRunner();
方法:
int update(Connection conn,String sql,Object… args);
T query(Connection conn,String sql,ResultSetHandler rsh,Object… args);
批量处理的方法
还有一个类:DbUtils
提交事务的方法:(这里的close不是销毁而是归还)
回滚事务的方法:
添加DBUtils的构建路径
DBUtils的添加操作:
@Test
/**
* 添加操作
*/
*public* *void* demo1() *throws* SQLException{
// 创建核心类:QueryRunner:
QueryRunner queryRunner = *new* QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update(“insert into account values (null,?,?)”, “ddd”,10000);
}
DBUtils的修改操作:
@Test
/**
* 修改操作
*/
*public* *void* demo2() *throws* SQLException{
// 创建核心类:
QueryRunner queryRunner = *new* QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update(“update account set name=?,money=? where id =?”, “eee”,20000,4);
}
DBUtils的删除操作:
@Test
/**
* 删除操作
*/
*public* *void* demo3() *throws* SQLException{
// 创建核心类:
QueryRunner queryRunner = *new* QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update(“delete from account where id = ?”, 3);
}
DBUtils的查询操作:
查询一条记录:
创建一个对象:Account(这个对象实例其实就是javabean)
也要重写toString方法
查询的代码实现:T query(String sql,ResultSetHandler rsh,Object… args);
查询多条语句:
ResultSetHandler接口的实现类:
\1. ArrayHandler和ArrayListHandler
ArrayHandler:将查询到的一条记录封装到一个数组当中,这个数组应该是Object[]
ArrayListHandler:将多条记录封装到一个装有Object[]的List集合中。
\2. BeanHandler和BeanListHandler(重要)
BeanHandler:将一条记录封装到一个JavaBean中。
必须保证Acoount中的变量和SQL中的列名一致
BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中。
\3. MapHandler和MapListHandler
MapHandler:将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值。
MapListHandler:将多条记录封装到一个装有Map的List集合中。
\4. ColumnListHandler、ScalarHandler、KeyedHandler
ColumnListHandler:将数据库中的某列的值封装到List集合中。
ScalarHandler:将单个值封装。
KeyedHandler(了解即可,用的很少):将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的(可以指定为数据库中的某一列名)。
因为可能是不同的类型,为了通用性,一般都用Object类型。