JDBC学习
JDBC
1.概述
JDBC(Java DataBase Connectivity)Java数据库连接(技术),也就是能够通过java代码,连接到数据库,并可以使用SQL语句,对数据库进行各种操作
最早连接数据库的标准还有 ODBC(Open Database Connectivity),开放式数据库连接。使用C语言实现。
它提供了一套连接并操作数据库的API接口,使得程序员可以使用同一套标准的代码,去访问不同数据库,而不需要改变代码。
1.1程序连接数据库的关键:
也就是说,如果我们想连接xxx数据库,就把xxx数据库驱动,注册到驱动管理器中即可,驱动管理器通过注册进来的驱动,获取到数据库的连接,然后就可以正常使用SQL语句对数据库进行操作了。
jdbc 不过是 odbc 的java实现版本
JDBC就相当于Java版本的ODBC
2.连接
在使用JDBC连接数据库之前,需要先获取到数据库的连接对象,而获取连接对象之前,需要先注册驱动到驱动管理器中,而注册驱动之前,需要先加载驱动到内存中。
- 加载驱动类
- 将驱动类注册到驱动管理器中(可以自动完成)
- 通过驱动管理器获取数据库连接对象
2.1加载驱动
java.sql.Driver
是JDBC中提供的驱动接口,每一种数据库的驱动类都要实现这个接口。
ava.sql.DriverManager
是JDBC中提供的驱动管理器(类),注册驱动后,可通过它获取到数据库连接对象。
可以看出,驱动管理器可以从注册的驱动中依次尝试,是否可以通过该驱动获得数据库连接对象,如果可以则返回,如果不可则尝试一下个驱动。
2.2加载驱动类的三种方式
常用的三种方式有:
@Test
public void testGetConnection() throws Exception {
// forName方式加载驱动类
Class.forName("oracle.jdbc.OracleDriver");
}
@Test
public void testRegistryDriver() throws Exception {
//创建出驱动对象
OracleDriver driver =
new OracleDriver();
// OracleDriver() 是在oracle.jdbc.driver包底下
//或者 oracle.jdbc 包底下
}
@Test
public void testRegistryDriver4() throws Exception{
//设置环境变量
//jdbc.drivers=oracle.jdbc.driver.OracleDriver
//驱动管理器会自动加载该驱动类
System.setProperty("jdbc.drivers", "oracle.jdbc.OracleDriver");
}
注意,驱动类使用oracle.jdbc.driver.OracleDriver或者oracle.jdbc.OracleDriver都是可以的
2.3获得连接
驱动类已经加载,那么接下来就可以使用驱动管理器获取数据库连接对象了
把驱动类注册到驱动管理器中,可以手动完成,也可以自动完成oracle.jdbc.driver.OracleDriver
驱动类,被加载之后,会自动注册到驱动管理器中。
DriverManager中会通过各种方式,加载并初始化驱动类,同时提供了getConnection方法,可以给我们返回数据库连接对象。
我们通过 DriverManger.getConnection 获得连接 。
但是获取数据库连接对象,需要三个参数:
- url,需要连接数据库的地址
- user,登录数据库的用户名
- password, 登录数据库的密码
连接不同数据库的时候,url地址是不用的,如果是oracle数据库,则url地址如下:
String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
其中:
- thin是oracle的一种连接方式
- 后边必须有数据库所在的主机的ip和端口号
- XE表示连接的数据库的名字
@Test
public void testGetConnection() throws Exception {
//1.注册驱动
Class.forName("oracle.jdbc.OracleDriver");
//2.通过驱动管理器获取与数据库的连接
// url jdbc: 数据库厂商的名字 连接方式:@ip地址 :数据库占用端口 : 数据库的名字 ;
String url ="jdbc:oracle:thin:@127.0.0.1:1521:XE";
String user="wpj";
String password ="wpj";
//获取连接
Connection connection = DriverManager.getConnection(url, user, password);
}
在获取到数据库连接对象之后,就可以对数据库进行各种操作了
2.4通过封装获取连接数据库的信息
通过Properties(); 对象可以完成对 Properties格式的文件 进行解析。 Properties 本身继承了 hashtable,所以拥有哈希表的特性, 用k与v存储数据。 用于分割的符号多种多样。个人推荐使用 等号 “=” 更方便于k值与v值的分割
jdbc.driver=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.user:user
jdbc.password:password
可以使用 Properties的load方法 将文件传入。load方法传入的是一个输入字节流。 load调用了load0方法 load0 具体实现了k值v值的分割与存储。
我们一般 是这样在传入
Properties properties = new Properties();
properties.load(new FileInputStream("src/datasource.properties"));
这是代表 datasource.properties
文件在项目的src包底下。
但是这有一个问题
当我们把项目打包之后,jar包中将不存在src目录,那文件又将何处去寻找呢?
当项目打包之后 该文件将与项目底下的第一个包同级。
一个类的加载的时候,从第一个包开始加载,说明 该文件与加载路径同级。
类名.getClass()得到Class类型对象
类名.getClass().getClassLoder(); 得到Class类型 类加载器
类名.getClass().getClassLoder().getResoure(“文件名”);得到文件路径
类名.getClass().getClassLoder().getResoureAsStream(“文件名”);这个是在src底下的文件,省去src不写 然后可以得到这个文件的流 。
Properties properties = new Properties();
properties.load(类名.getClass.getClassLoder().getResourceAsStream("datasource.properties"));
此时properties对象里面已经存储着 datasource文件的信息
通过properties.getProperty()方法传k获取value
driverClassName= properties.getProperty(“jdbc.driver”);
driverClassName 存储着 驱动类的名字 用于后面注册驱动。
url=properties.getProperty(“url”);
3.通用步骤
使用JDBC 对数据库进行操作的通用步骤,应该包括以下几个步骤:
- 注册驱动
- 获取连接
- 创建数据库连接对象(statement对象或其子类对象)
- 执行SQL语句
- 处理结果集(一般是查询语句才需要处理)
- 资源关闭
其中,java.sql.Statement接口或者子类型接口,可以用来执行sql语句,常用的方法有:
- boolean execute(String sql) throws SQLException;
执行sql,返回 Boolean类型数据
true表示返回的结果是一个ResultSet
flase 表示返回结果是一个数据更新的条数或者没有返回结果 - int executeUpdate(String sql ) throws SQLException;
执行sql,返回int类型数据
返回0 ,则表示没有结果返回
返回其他数字,则返回操作了几条数据。
- ResultSet executeQuery(String sql ) throws SQLException;
执行sql,返回ResultSet类型结果
一般查询语句使用该方法,可以返回结果集ResultSet,然后再遍历结果集,拿到每条数据 - int executeBatch ) throws Exception;
执行批处理操作,返回每条sql命令的执行结果,放到int数字中。
其中,java.sql.ResultSet接口,专门用来表示select查询语句返回的结果集,表示当前sql的查询结果。
3.1 DDL与DML
实现:
@Test
public void createTableTest() throws Exception {
// 1.注册驱动
Class.forName(driverClassName);
// 2.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
// 3.创建Statement
Statement st = connection.createStatement();
// 4.执行sql
String sql = "create table kk(" +
"id number primary key," +
"name varchar2(25) constraint kk_name_nn not null"+
")";
/*
* exctue(sql) boolean 代表执行这条sql有没有结果集
* executeUpdate(sql)
* int 代表影响表的记录数
* executeQuery(sql)
* Resultset 返回查询的结果集
*
* */
st.execute(sql);
// 5.关闭资源
st.close();
connection.close();
System.out.println("执行成功");
}
创建,修改以上大同小异,只需要修改sql语句和statement对象的方法就行。结果没什么区别。修改使用的是
statement.executeUpdate 多一个int返回结果。可以利用,也可以不利用。
3.2 DQL
而查询就较为复杂些,需要处理结果集:
@Test
public void selectTest() {
//1.注册驱动
System.setProperty("jdbc.drivers",driverClassName);
//2.获取连接
try {
Connection connection = DriverManager.getConnection(url,user,password);
//3.创建搬运工
Statement ste =connection.createStatement();
//4.执行sql语句
String sql= "select * from kk";
//查询的所有的结果都在这里面
ResultSet resultSet = ste.executeQuery(sql);
//5.处理结果集
// 如果有下一个结果 指针就往下走
// 如果有下一行数据返回true 否则返回false
while(resultSet.next()) {
//获取第几列的数据 从1开始
long id = resultSet.getLong(1);
String name = resultSet.getString(2);
System.out.println(id+":"+name);
//获取指定(列名是查询出来数据的列名)列名的数据 如果没有这个名字就会报列名无效
long id = resultSet.getLong("id");
String name = resultSet.getString("name");
System.out.println(id+":"+name);
}
//6.关闭资源
resultSet.close();
ste.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
ResultSet对象提供了 get+数据类型();
的方法 可以传入列的序号,来表示获取第几列的数据,注意,从1开始。也根据可以查询结果中的字段名来查询,例如查询结果中有一列为 IDS , 那我们则可以get+数据类型("IDS");
去获取数据。假设IDS 为int类型 则完整语句为:
int ids = resultSet.getInt("IDS");
由此获得IDS字段的值。
3.2 Batch
获取到数据库对象之后,可以进行批处理
@Test
public void testBatch() throws Exception {
Class.forName(driverClassName);
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String sql ="insert into kk values(kk_seq.nextval,'花好')";
String sql1 = "delete from kk where id >2";
String sql2 ="update kk set name ='liuNeng' where id =1";
statement.addBatch(sql);
statement.addBatch(sql2);
statement.addBatch(sql1);
int[] is = statement.executeBatch();
for (int i : is) {
System.out.println(i);
}
statement.close();
connection.close();
}
4.statement
java.sql.statement
接口,是专门用来 执行SQL语句的,该接口还有俩个子接口:
java.sql.PreparedStatement
java.sql.CallableStatement
- CallableStatement接口继承了PreparedStatement接口
- PreparedStatement接口又继承了Statement接口
4.1 PrepareStatement
PrepareStatement
接口,简称ps,它除了拥有Statement
的功能特点之外,它还有有着自己的特点:可以对sql语句进行预处理。
Statement
,是每次执行一个sql语句,就要把一个完成的sql语句发送给数据库进行执行,然后取回返回的结果。
PrepareStatement
,可以把一个sql语句的结构,提前发送给数据库进行预处理,然后在专门给发送要操作的具体的值,在数据量大的时候,这种方式会大大提高执行效率。
4.1.1 使用
@Test
public void testPS() throws Exception {
// 注册驱动
Class.forName(driverClassName);
// 获取连接,这里我提前设置好了静态变量url,user,password ,并未他们附过值。
// getConnection方法需要一些连接数据库的数据
// url=jdbc:oracle:@localhost:1521:xe;
// user与password 就是数据库的用户名和密码
Connection connection = DriverManager.getConnection(url, user, password);
// 在PreparedStatement 中 该Statement子类对象 是先将sql语句传入数据库中进行校验,校验无误之后再传变量具体的值
// 所以sql语句中的变量值可以用占位符来先进行代替,暂时不写具体的内容是什么。
// 如下
String sql ="insert into kk values(kk_seq.nextval,?)";// 该插入语句values中的第二列数据暂时不写,用一个问号作为占位符表示之后再写。
// 只要成功创建ps,ps对象就会把sql语句发送数据库进行预处理
PreparedStatement ps = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for (int i = 1000; i < 2000; i++) {
//给sql语句替换占位符
// 添加String类型的数据 setString
// 第一个问号,具体是“陈卓璇”拼接i
ps.setString(1, "陈卓璇"+i);
// 执行
ps.execute();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
ps.close();
connection.close();
}
4.1.2 批处理
同样的PrepareStatement 也有批处理的方法:
public void testPsBatch() throws Exception {
Class.forName(driverClassName);
Connection connection = DriverManager.getConnection(url, user, password);
// 表面本身不能用占位符 只有数据可以
String sql ="insert into kk values(?,?,?,?)";
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < 20; i++) {
ps.setInt(1, i);
ps.setString(2, "dy"+i);
ps.setInt(3, i);
ps.setDate(4, new Date(System.currentTimeMillis()));
// 结构相同,值不同
// 将sql语句具体赋值之后的语句存入类似语句池一样的批处理池,等待执行。
ps.addBatch();
if(i%200==0){
// 每俩百条执行一次
ps.executeBatch();
}
}
// 将批处理池中剩余的所有的sql语句全部执行
ps.executeBatch();
ps.close();
connection.close();
}
4.2 CallableStatement
CallableStatement
接口,简称CS,它除了拥有俩个父接口的功能特点之外,它还有有着自己的特点:可以调用数据库中的存储过程。(了解即可)
5.线程池
5.1 概述
池技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资源的开销。
例如,数据库连接池,在系统初始化时创建一定数量数据库连接对象,需要时直接从池中取出一个空闲对象,用完后并不直接释放掉对象,而是再放到对象池中,以便下一次对象请求可以直接复用。
这样可以消除对象创建和销毁所带来的延迟,从而提高系统的性能。
在当前数据库环境下,创建出一定数据的数据库之后,就无法再创建新的对象了。
而数据库连接池,就可以在种情况下,预先创建出一批数据库连接对象,然后对它们进行管理,并反复使用,这样就不需要频繁的销毁和创建了,提供了资源的利用率。
5.2 druid
druid 是阿里巴巴开源的数据库连接池项目
javax.sql.DataSource
是Java中定义的一个数据源标准的接口,通过它获取到的数据库连接对象,如果调用close()方法,不会再关闭连接,而是归还连接到连接池中。
相比 之前 DateSource.properties;
有驱动类名 url 用户名 用户密码 还多了 连接池 默认连接数(initialSize)和 最大连接数(MaxActive) (MaxActive不超过18 原则上 尽量远离18 占用太多可能会导致其他程序用不了而出错)。
当然还有一些其他的设置,如最大等待时间等。
具体druid中的配置信息的key是固定的,com.alibaba.druid.pool.DruidDataSourceFactory
类中定义了静态常量,记录了这些key的名字。