第二十七天 数据库基础&JDBC使用&工具类Properties&连接池&DBUtils【悟空教程】
第27天 数据库基础&JDBC使用&工具类Properties&连接池&DBUtils
数据库就是存储数据的仓库,其本质是一个文件系统,数据库按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作。
常见的数据库
MySQL:开源免费的数据库,小型的数据库.已经被Oracle收购了.MySQL6.x版本也开始收费。
Oracle:收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。
DB2:IBM公司的数据库产品,收费的。常应用在银行系统中.
SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。
SyBase:已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。
SQLite:嵌入式的小型数据库,应用在手机端。
Java常用的数据库:MySQL,Oracle。
这里使用MySQL数据库。
MySQL中可以有多个数据库,数据库是真正存储数据的地方。
数据库中以表为组织单位存储数据。
表类似我们的Java类,每个字段都有对应的数据类型。
那么用我们熟悉的java程序来与关系型数据对比,就会发现以下对应关系。
类----------表
类中属性----------表中字段
对象----------记录
根据表字段所规定的数据类型,我们可以向其中填入一条条的数据,而表中的每条数据类似类的实例对象。表中的一行一行的信息我们称之为记录。
财务表
账务ID | 所属大分类 | 金额 | 小分类 | 账户 | 创建时间 | 账务描述 |
---|---|---|---|---|---|---|
1 | 收入 | 15800 | 彩票收入 | 招商银行 | 2015-12-31 | 年末收入 |
2 | 支出 | 19800 | 水电支出 | 交通银行 | 2016-01-01 | 电费1000 |
3 | 支出 | 12800 | 奢侈品支出 | 现金 | 2016-01-02 | 换个爱马 |
用户表
用户ID | 用户名称 | 用户年龄 |
---|---|---|
1 | lisi | 23 |
2 | wang | 24 |
3 | 赵六 | 48 |
进而有类似如下的对应关系:
数据库是不认识JAVA语言的,但是我们同样要与数据库交互,这时需要使用到数据库认识的语言SQL语句,它是数据库的代码。
结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
创建数据库、创建数据表、向数据表中添加一条条数据信息均需要使用SQL语句。
官网下载安装文档:www.javahelp.com.cn
安装后,MySQL会以windows服务的方式为我们提供数据存储功能。开启和关闭服务的操作:右键点击我的电脑→管理→服务→可以找到MySQL服务开启或关闭
MySQL是一个需要账户名密码登录的数据库,登陆后使用,它提供了一个默认的root账号,使用安装时设置的密码即可登录。
格式:cmd> mysql –u用户名 –p
密码
例如:mysql -uroot –p
密码
输入用户名、密码,点击连接按钮,进行访问MySQL数据库进行操作
在Query窗口中,输入SQL代码,选中要执行的SQL代码,按F8键运行,或按执行按钮运行。
类型 | 描述 |
---|---|
int | 整型 |
double | 浮点型 |
varchar | 字符串型 |
date | 日期类型,格式为yyyy-MM-dd,只有年月日,没有时分秒 |
详详细的数据类型(不建议详细阅读!)
分类 | 类型名称 | 说明 |
---|---|---|
整数类型 | tinyInt | 很小的整数 |
smallint | 小的整数 | |
mediumint | 中等大小的整数 | |
int(integer) | 普通大小的整数 | |
小数类型 | float | 单精度浮点数 |
double | 双精度浮点数 | |
decimal(m,d) | 压缩严格的定点数 | |
日期类型 | year | YYYY 1901~2155 |
time | HH:MM:SS -838:59:59~838:59:59 | |
date | YYYY-MM-DD 1000-01-01~9999-12-31 | |
datetime | YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59 | |
timestamp | YYYY-MM-DD HH:MM:SS 1970~01~01 00:00:01 UTC~2038-01-19 03:14:07UTC | |
文本、二进制类型 | CHAR(M) | M为0~255之间的整数 |
VARCHAR(M) | M为0~65535之间的整数 | |
TINYBLOB | 允许长度0~255字节 | |
BLOB | 允许长度0~65535字节 | |
MEDIUMBLOB | 允许长度0~167772150字节 | |
LONGBLOB | 允许长度0~4,294,967,295字节 | |
TINYTEXT | 允许长度0~255字节 | |
TEXT | 允许长度0~65535字节 | |
MEDIUMTEXT | 允许长度0~167772150字节 | |
LONGTEXT | 允许长度0~4294967295字节 | |
VARBINARY(M) | 允许长度0~M个字节的变长字节字符串 | |
BINARY(M) | 允许长度0~M个字节的定长字节字符串 |
a) 数据库相关语句
create database 数据库名; 创建数据库
show databases; 查看数据库
show create database 数据库名; 查看建库语句
drop database 数据库名; 删除数据库
use 数据库名; 使用指定数据库
b) 表结构相关语句
create table 表名(
字段1 类型,
字段2 类型,
...
)
主键是用于标识当前记录的字段。它的特点是非空,唯一。
在开发中一般情况下主键是不具备任何含义,只是用于标识当前记录。
1.在创建表时创建主键,在字段后面加上 primary key.
create table tablename(
id int primary key,
.......
)
2. 在创建表时创建主键,在表创建的最后来指定主键
create table tablename(
id int,
.......,
primary key(id)
)
3.删除主键:alter table 表名 drop primary key ;
4.主键自动增长:一般主键是自增长的字段,不需要指定。
添加自增长语句 主键字段后加auto_increment(只适用MySQL)
其他约束:其他约束还有如外键、唯一、非空等基础加强中详解
desc 表名; 查看某个现有表的结构
show tables; 查看当前数据库下所有的表
show create table 表名; 查看建表语句
rename table 旧表名 to 新表名 修改表名
alter table 表名 add 列名 类型; 添加列操作
alter table 表名 modify 列名 类型; 修改列类型
alter table 表名 drop 列名; 删除列
alter table 表名 change 旧列名 新列名 类型; 修改列名称
drop table 表名; 删除表
truncate 表名; 摧毁表(删除表)
c) 表数据相关语句
insert into 表名(列名1,列名2, ...) values(列值1, 列值2, ...); 指定列插入
insert into 表名 values(列值1, 列值2, ...); 不指定列插入(所有列)
注意:
插入的数据应与字段的数据类型相同
数据的大小应该在列的长度范围内
在values中列出的数据位置必须与被加入列的排列位置相对应。
除了数值类型外,其它的字段类型的值必须使用 单引号引起。
如果要插入空值,可以不写字段,或者插入 null。
对于自动增长的列在操作时,直接插入null值即可。
update 表名 set 字段=值 ,... where 条件 修改指定条件的数据,将这条数据的指定列修改为指定值
delete from 表名 where 条件 删除指定条件的数据
delete from 表名 删除表内所有数据
truncate table 表名 摧毁表再重新创建表,达到删除表内所有数据
select 字段1,字段2,...from 表名; 查询指定字段信息
select * from 表名; 查询表中所有字段
注意:使用"*"在练习,学习过程中可以使用,在实际开发中,不建议使用。
select distinct 字段 from 表名; distinct用于去除重复
使用as 别名可以给表中的字段,表设置别名. 当查询语句复杂时,使用别名可以极大的简便操作。
select 字段 as 别名,字段 AS 别名 from 表名;
我们在sql操作中,可以直接对列进行运算。
where语句表条件过滤。满足条件操作,不满足不操作,多用于数据的查询与修改。
格式 :
select 字段 from 表名 where 条件;
select 字段 from 表名 where name=’香蕉’;
select 字段 from 表名 where price<5;
select 字段 from 表名 where name>5’;
1.比较运算符
> >= < <= = !=(<>)
2.逻辑运算符
And(并且) or (或者) not(取反)
3.between ...and...
在指定范围内
注意:between 后面的值必须是小值 and后面的是大值
4.in
可以比较多个值
5.like
模糊查询
通配符使用:
1.% 匹配多个
2._ 匹配一个
select 字段 from 表名 where 字段 like ‘%果’; #任意个字符+果,以果结尾
select 字段 from 表名 where 字段 like ‘果%’; #果+任意个字符,以果开头
select 字段 from 表名 where 字段 like ‘%果%’; #包含果
select 字段 from 表名 where 字段 like ‘_果’; #某果
select 字段 from 表名 where 字段 like ‘_果_’; #某果某
6.null值操作
is null; 判断为空
is not null; 判断不为空
查询例句如下:
查询所有支出记录
SELECT * FROM gjp_ledger WHERE parent = '支出';
查询出金额价格大于1000的信息
SELECT * FROM gjp_ledger WHERE money >1000;
查询出金额在2000-5000之间的账务信息
SELECT * FROM gjp_ledger WHERE money >=2000 AND money <=5000;
SELECT * FROM gjp_ledger WHERE money BETWEEN 2000 AND 5000;
查询出金额是1000或5000或3500的商品信息
SELECT * FROM gjp_ledger WHERE money =1000 OR money =5000 OR money =3500;
SELECT * FROM gjp_ledger WHERE money IN(1000,5000,3500);
查询出账务的银行有工商银行的账务信息。
SELECT * FROM gjp_ledger WHERE account LIKE "%工商%";
查询出账务描述中是两个字的账务信息
SELECT * FROM gjp_ledger WHERE ldesc LIKE "__";
查询出账务accont不为null账务信息
SELECT * FROM gjp_ledger WHERE account IS NOT NULL;
SELECT * FROM gjp_ledger WHERE NOT account IS NULL;
order by可以将查询出的结果进行排序。放置在select语句的最后。
ASC 升序 (默认)
DESC 降序
格式:
SELECT * FROM 表名 ORDER BY 字段ASC;
SELECT * FROM 表名 ORDER BY 字段DESC;
SELECT * FROM 表名 ORDER BY 字段DESC LIMIT 3;
查询字段中最贵的3个。(逻辑倒叙排列取三个值)
之前我们做的查询都是横向查询,它们都是根据条件一行一行的进行判断,而使用聚合函数查询是纵向查询,它是对一列的值进行计算,然后返回一个单一的值;另外聚合函数会忽略空值。
今天我们学习如下五个聚合函数:
count:统计指定列不为NULL的记录行数;
格式:select count(字段) from 表名;
sum:计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
max:计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
min:计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
avg:计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;
格式:
select avg(字段) from 表名;
select avg(字段) as ‘平均价格’ from 表名;
查询例句如下:
统计账务表中共有多少条记录
SELECT COUNT(*) FROM gjp_ledger;
统计账务表中金额大于3000的有多少条记录
SELECT COUNT(*) FROM gjp_ledger WHERE money>3000;
统计有多少收入的账务信息
SELECT SUM(money) FROM gjp_ledger WHERE parent = '收入';
SELECT SUM(IFNULL(money,0)) FROM gjp_ledger WHERE parent = '收入';
统计出记录中支出的平均值
SELECT AVG(money) FROM gjp_ledger where parent = '支出';
统计出商品表中收入的最大与最小值
SELECT MAX(money),MIN(money) FROM gjp_ledger;
SELECT MAX(money),MIN(IFNULL(money,0)) FROM gjp_ledger;
分组查询是指使用group by字句对查询信息进行分组,例如:我们要统计出gjp_ledger表中所有分类账务的总数量,这时就需要使用group by 来对gjp_ledger表中的商品根据parent进行分组操作。
分组后我们在对每一组数据进行统计。
格式:
select 分组的字段 ,avg(计算的字段) from 表名 group by 分组的字段;
分组操作中的having子名是用于在分组后对数据进行过滤的,作用类似于where条件。
having与where的区别:
1.having是在分组后对数据进行过滤.
where是在分组前对数据进行过滤
格式:select 分组的字段 ,avg(计算的字段) from 表名 where 计算的字段>20 group by 分组的字段 having avg(计算的字段)>20;
逻辑:先确定where语句 然后在确定having语句
2.having后面可以使用分组函数(统计函数)
where后面不可以使用分组函数。
查询例句如下:
对账务分类别统计,求出每一种类账务的总金额
SELECT parent,SUM(money) FROM gjp_ledger GROUP BY parent;
对账务分类别统计,求出每一种类账务的总金额,金额要大于20000才显示
SELECT parent,SUM(money) FROM gjp_ledger GROUP BY parent HAVING SUM(money)>2000;
我们在dos命令行操作中文时,会报错
insert into user(username,password) values(‘张三’,’123’);
ERROR 1366 (HY000): Incorrect string value: '\xD5\xC5\xC8\xFD' for column 'username' at row 1
原因:
因为mysql的客户端编码的问题我们的是utf8,而系统的cmd窗口编码是gbk
解决方案(临时解决方案):
修改mysql客户端编码。
show variables like 'character%'; 查看所有mysql的编码
在图中与客户端有关的编码设置:
client connetion result 和客户端相关
database server system 和服务器端相关
将客户端编码修改为gbk.
set character_set_results=gbk; / set names gbk;
以上操作,只针对当前窗口有效果,如果关闭了服务器便失效。
如果想要永久修改,通过以下方式:
在mysql安装目录下有my.ini文件
default-character-set=gbk 客户端编码设置
character-set-server=utf8 服务器端编码设置
注意:修改完成配置文件,重启服务
1:完成数据库表的创建并插入数据、读取数据
2:完成课上所有SQL练习
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
今天我们使用的是mysql的驱动mysql-connector-java-5.1.37-bin.jar
Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。
JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
1. 注册驱动
2. 获得连接
3. 获得语句执行平台
4. 执行sql语句
5. 处理结果
6. 释放资源
创建lib目录,用于存放当前项目需要的所有jar包
选择jar包,右键执行build path / Add to Build Path
代码:Class.forName("com.mysql.jdbc.Driver");
JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver
DriverManager工具类,提供注册驱动的方法 registerDriver(),方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代码不推荐使用,存在两方面不足
1. 硬编码,后期不易于程序扩展和维护
2. 驱动被注册两次。
通常开发我们使用Class.forName() 加载一个使用字符串描述的驱动类。
如果使用Class.forName()将类加载到内存,该类的静态代码将自动执行。
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
……
}
import java.sql.SQLException;
import com.mysql.jdbc.Driver;
/*
* 注册 驱动 : 加载 Driver 类 就可以注册了. new Driver(),虽然可以加载类完成注册,但是并不好.
* 用反射的方式加载Driver类.
* Class.forName(类名);
*/
public class Demo {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
System.out.println("连接数据库...... ");
Driver driver = new Driver();
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
System.out.println("注册成功");
}
}
代码:Connection con = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb”,”root”,”123”);
获取连接需要方法 DriverManager.getConnection(url,username,password),三个参数分别表示,url 需要连接数据库的位置(网址) user用户名 password 密码
url比较复杂,下面是mysql的url:
jdbc:mysql://localhost:3306/mydb
JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
String sql = "某SQL语句";
获取Statement语句执行平台:Statement stmt = con.createStatement();
常用方法:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/*
* 获取连接
* DriverManager.getConnection(url, user, password)
* url : 数据的地址.
*
* jdbc:mysql://localhost:3306/mydb
* jdbc: 写死的.
* mysql:写死的.(目前)
* 库的地址 :localhost 本机, 3306 数据库端口. mydb(你指定的库名字)
* user:数据库管理员用户名(root)
* password: 就是root 的 密码. (root)
*/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/bj_230";
String user = "root";
String password = "root" ;
// 2. 获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection); //com.mysql.jdbc.JDBC4Connection@be8464
// 3. 获取 sql 语句的执行者 .
Statement stat = connection.createStatement();
// 4. 执行 sql
String sql = "INSERT INTO goods VALUES(3,'草莓',15,'斤','昌平' )"; // 插入一条记录
int executeUpdate = stat.executeUpdate(sql);
System.out.println("插入记录成功");
//5------------------------------------------
System.out.println(executeUpdate);
// 6. 关闭资源
stat.close();
connection.close();
}
}
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的
1.getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
2.getXXX(String columnName)方法,通过列名字获取指定列的数据。
rs.next();//指向第一行
rs.getInt(1);//获取第一行第一列的数据
常用方法:
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close();
stmt.close();
con.close();
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/*
* 修改一条记录.
*
* id 为3记录, gname 修改为 桃子 , 产地 修改为平谷, 价格修改为 10块.
*/
public class Test {
public static void main(String[] args) throws Exception {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/bj_230";
String user ="root" ;
String password = "root" ;
Connection con = DriverManager.getConnection(url, user, password);
// 3.获取 sql语句执行者
Statement stat = con.createStatement();
// 4.执行sql executeUpdate()
String sql ="UPDATE goods SET gname='桃子',area='平谷' ,price=10 WHERE id=3";
stat.executeUpdate(sql);
// 5.没有
// 6.释放资源
stat.close();
con.close();
System.out.println("修改成功 ");
}
}
案例:
假设有登录案例SQL语句如下:
SELECT * FROM 用户表
WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号u密码后,查询到了信息则让用户登录。
但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a时
则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ‘a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
为此,我们使用PreparedStatement来解决对应的问题。
使用处理对象时,建议每条sql语句所有的实际参数,都使用逗号分隔。
String sql = "insert into category(cid,cname) values(?,?)";;
PreparedStatement预处理对象代码:PreparedStatement psmt = conn.prepareStatement(sql)
常用方法:
1. 执行SQL语句:
2. 设置实际参数
public class JDBCTest {
public static void main(String[] args) {
try {
//1,注册驱动
/*
* 加载MySql的Driver类到内存,MySql的Driver类在自己的静态代码块实现注册驱动的操作
* 1.使用类加载器,完成MySql的Driver类的加载
* Class.forName( 要加载的类名 ); //类名为带有包名的完整路径名称
*/
Class.forName("com.mysql.jdbc.Driver");
//2,获取连接
/*
* 参数1: 要连接的数据库地址 jdbc:mysql://localhost:3306/mydb, 最后的mydb为数据库名称,我们可以改变
* 参数2: 连接数据库时 使用的用户名
* 参数3: 连接数据库时 使用的密码
*/
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/day22_JDBC", "root", "root");
//3,获取SQL语句执行者
Statement stat = conn.createStatement();
//4,执行SQL语句
String sql = "SELECT * FROM sort";
ResultSet resultSet = stat.executeQuery(sql);
//5,处理结果集(遍历结果集合)
while( resultSet.next() ){
//获取当前行的分类ID
String sid = resultSet.getString("sid");//方法参数为数据库表中的列名
//获取当前行的分类名称
String sname = resultSet.getString("sname");
//显示数据
System.out.println(sid+"-----"+sname);
}
//6,释放资源
/*
* 释放resultSet
* 释放Statement
* 释放Connection
*/
resultSet.close();
stat.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
“获得连接”将在之后的增删改查所有功能中都存在,可以封装工具类JDBCUtils提供获取连接方法从而达到代码的重复利用。该工具类提供方法:public static Connection getConnection()。
package cn.com.javahelp;
import java.sql.Connection;
import java.sql.DriverManager;
/*
* JDBC工具类
*/
public class JDBCUtil {
static String driverName = "com.mysql.jdbc.Driver";
static String url = "jdbc:mysql://localhost:3306/mydatabase";
static String user = "root";
static String password = "123";
static {
// 1. 注册驱动
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
System.out.println("数据库驱动注册失败!");
}
}
/**
* 提供获取连接的方法
* @return 连接对象
* @throws Exception
*/
public static Connection getConn() throws Exception {
// 2. 获得连接
Connection conn = DriverManager.getConnection(url,user,password);
//返回连接
return conn;
}
}
开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
通常情况下,我们习惯使用properties文件,此文件我们将做如下要求:
1. 文件位置:任意,在指定位置,一般在项目根目录下
2. 文件名称:任意,扩展名为properties
3. 文件内容:一行一组数据,格式是“key=value”.
a) key命名自定义,如果是多个单词,习惯使用点分隔。(习惯性可以使用静态常量方式记录固定的key)例如:jdbc.driver
b) value值不支持中文,如果需要使用非英文字符,将进行unicode转换。(如果没有指定编码,并且默认编码与properties不同,或者使用properties文件默认的iso8859-1会出现乱码)
右键/New/File,输入“db.properties”文件名。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.user=root
jdbc.password=123
对应properties文件处理,开发中也使用Properties对象进行。我们将采用加载properties文件获得流,然后使用Properties对象进行处理。
static{
try {
//1 使用Properties处理流
// * 使用load()方法加载指定的流
Properties props = new Properties();
props.load(is);
//2 使用getProperty(key),通过key获得需要的值,
driver = props.getProperty("jdbc.driver");
url = props.getProperty("jdbc.url");
user = props.getProperty("jdbc.user");
password = props.getProperty("jdbc.password");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获得连接
* @return
*/
public static Connection getConnection(){
try {
//1 注册驱动
Class.forName(driver);
//2 获得连接
Connection conn = DriverManager.getConnection(url,user,password);
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
1:完成JDBC步骤的课上查询语句,集合昨天学习的多样SQL需求
2:参照课上的查询操作executeQuery,自己完成增删改的操作executeUpdate
如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache commons组织的一个成员:DBUtils。
DBUtils就是JDBC的简化开发工具包。需要使用技术:连接池(获得连接),SQL语句都没有少。
JavaBean就是一个类,在开发中常用于封装数据。具有如下特性
1. 需要实现接口:java.io.Serializable ,通常偷懒省略了。
2. 提供私有字段:private 类型 字段名
3. 提供getter/setter方法
4. 提供无参构造
public class Category {
private String cid;
private String cname;
public Category() {
super();
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
…toString….
}
DBUitls是Apache组织提供的一个对JDBC进行简单封装的开源工具类,使用DBUItils工具类对数据库进行开发, 使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
DBUtils操作的是数据库表数据,不会使用其进行表结构字段的设置,因为数据库的定义通常是预先已经设置好了。
Dbutils三个核心类介绍
DBUtils的使用是相对固定的步骤,是常规Java项目的基本操作。
常用Handler(处理方式):
ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
---|---|
ArrayListHandler | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中 |
BeanHandler | 将结果集中第一条记录封装到一个指定的javaBean中。 |
BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中 |
ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
KeyedHandler | 将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。 |
MapHandler | 将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值 |
MapListHandler | 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中 |
ScalarHandler | 它是用于单数据。例如select count(*) from 表操作 |
在查询数据的基本步骤基础上,使用QueryRunner调用update(String sql, Object... params) ,执行增删改操作。前边的sql可以添加?,其后边的可变参数为前边的?赋值。
在增删改操作中,没有对结果集的操作。
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池。
常见的连接池:DBCP、C3P0。
这里学习DBCP连接池。C3P0连接池在就业班学习。
当多个程序(多个用户)的多个需求时,我们需要创建多个Connection对象,而与数据库创建连接是十分消耗资源的,连接池创建了多个连接,当使用连接操作数据时,会分配一个连接,而这个连接也可供其他程序(用户)的数据库操作使用,类似线程池。
javax.sql包下的 DataSource是线程池接口。可以使用getConnection()方法获取一个连接,如果连接对象Connection是通过连接池获取的,当通过Connection对象调用close()方法时,不再是销毁连接对象,而是将连接对象放回到连接池。
不使用连接池
使用连接池
a) dbcp连接池介绍
DBCP 是 Apache 软件基金组织下的一个优秀的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
b) dbcp连接池配置
//导包加入classpath
// 1.创建连接池对象
BasicDataSource ds = new BasicDataSource();
// 2.设置相关属性
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("xxx");
ds.setUsername("xxx");
ds.setPassword("xxx");
c) DBUtils使用连接池
当拥有了连接池后,我们可以直接使用连接池获取连接对象,完成普通的sql操作。(在获取连接后,关闭连接是将连接返还给连接池)
而在DBUtils的学习过程中,我们使用QueryRunner的空参构造完成,每次请求调用query方法时传入连接对象。
而当有了线程池后,可以调用QueryRunner的带参构造创建对象:
QueryRunner(DataSource connectionPool);
这时,在每次访问数据库时,将无需再传入连接对象。因为在每次使用QueryRunner时,都已经通过连接池获取了连接。
如:
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/javahelp_gjp");//设置数据库连接
ds.setDriverClassName("com.mysql.jdbc.Driver");//设置驱动名称
ds.setUsername("root");//设置登录用户名
ds.setPassword("abc");//设置登录密码
ds.setMaxIdle(3);//连接池最大空闲连接个数
ds.setMaxWait(3000);//连接池最大等待时间
ds.setMaxActive(5);//连接池最大连接个数
ds.setInitialSize(3);//连接池初始化连接个数
QueryRunner qr = new QueryRunner(ds);
//再查询时无需传入连接对象,因为QueryRunner对象已经从连接池中获取了连接
返回值类型 query = qr.query(SQL语句,Handler对象);
/*
* 创建DBCP连接池工具类
*
* 作用: 配置DBCP连接池,提供获取连接池中连接对象的方法
*/
public class DBCPUtils {
//创建连接池
private static BasicDataSource dataSource = new BasicDataSource();
public static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/day22_jdbc";
public static final String USER_NAME = "root";
public static final String PASSWORD = "root";
//通过静态代码块,完成dataSource对象的信息配置
static {
//指定驱动名称
dataSource.setDriverClassName(DRIVER_CLASS_NAME);
//指定数据库URL
dataSource.setUrl(URL);
//指定数据库连接的用户名
dataSource.setUsername(USER_NAME);
//指定数据库连接的密码
dataSource.setPassword(PASSWORD);
}
/*
* 提供获取连接池中连接对象的方法
*/
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
分类 | 属性 | 描述 |
---|---|---|
必须项 | driverClassName | 数据库驱动名称 |
url | 数据库的地址 | |
username | 用户名 | |
password | 密码 | |
基本项 | maxActive | 最大连接数量 |
minIdle | 最小空闲连接 | |
maxIdle | 最大空闲连接 | |
initialSize | 初始化连接 | |
优化配置(扩展) | logAbandoned | 连接被泄露时是否打印 |
removeAbandoned | 是否自动回收超时连接 | |
removeAbandonedTimeout | 超时时间(以秒数为单位) | |
maxWait | 超时等待时间以毫秒为单位 1000等于60秒 | |
timeBetweenEvictionRunsMillis | 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位 | |
numTestsPerEvictionRun | 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 | |
minEvictableIdleTimeMillis | 连接在池中保持空闲而不被空闲连接回收器线程 |
参考文档:http://commons.apache.org/proper/commons-dbcp/configuration.html
1:定义MyDBUtils类,提供连接池方法(提示:所有连接池的属性设置项均可以为静态成员变量)
2:使用QueryRunner带预处理的查询或更新方法,将where条件的值使用?的方式传入
3:分别使用连接池与不使用连接池操作QueryRunner,完成查询。
4:将所有Handler对象使用一遍。sql语句参照day27的课上语句。