JDBC总结
JDBC
JAVA Database Connectivity java 数据库连接
1.1数据库访问的过程
- 客户端与Mysql服务器之间建立连接
- 客户端向Mysql服务器发送数据库请求
- Mysql服务器处理客户端请求,并返回结果给客户端
- 客户端接受Mysql服务器的响应,并按照自己的业务逻辑做响应处理。
释放相关资源。
1.2 JDBC概述
Java DataBase Connecitivity,java数据库连接。Java程序访问数据库的方法。
Sun公司制定了一套java语言访问数据库的规范。(接口)不能进行编程
各大数据库生产厂商来实现相应的实现类。
组成JDBC的2个包:
- java.sql.*:是核心包里的,是jdbc2.0之前的东西
- javax.sql.*:是扩展包中的,包括了jdbc3.0的特性
1.3编写一个JDBC程序
- 搭建实验环境
- 在mysql中创建一个数据库
- 新建一个java工程,并导入数据驱动
需要下载下Java连接Mysql 的驱动包 https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.17
导包 :导入下载的jar包,导包的时候level是project级别的时候,在其他module中不需要再重复导包,。
-
编写程序,在程序中加载数据库驱动
DriverManager.registerDiver(Driver dirver) -
建立连接(connection)
Connection conn =DiverManager.getConnection(url,user,pass);
url的写法:
常用数据库URL地址的写法:
Oracle写法:jdbc:oracle:thin:@localhost:1521:dbname
SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=dbname
MySql—------jdbc:mysql://localhost:3306/dbname
Mysql的url地址的简写形式: jdbc:mysql:///sid
如果你的主机地址默认是localhost 端口是3306 -
创建一个用于向数据库大宋SQL的Statement对象,并发送SQL
Statement st=conn.createQuery();
Resultset rs=st.executeQuery(sql); -
从代表结果集的 ResultSet中取出数据,打印到命令行窗口
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println("id="+id + "=name="+name+"age="+age) } -
断开和数据库的连接,并释放资源
import com.mysql.jdbc.Driver;
import java.sql.*;
public class Demo_01 {
public static void main(String[] args) {
try {
DriverManager.registerDriver(new Driver());
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/mydemo?characterEncoding=utf8","root","123456");
//创建发送sql对象的statement对象
Statement st = conn.createStatement();
//发送sql
ResultSet rt = st.executeQuery("select * from student");
rt.next();
String name= rt.getString("name");
System.out.println(name);
rt.close();
conn.close();
rt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1.4 对第一个JDBC程序的优化
-
DriverManager.registerDriver(new Driver());通过查看Driver源码可以看到,这段代码其实执行了两次驱动注册,所以可以进一步的进行优化:Class.forName("com.mysql.jdbc.Driver");
-
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb4?characterEncoding=utf8", "root", "123456");
存在硬编码,如果今后数据库的用户名或者密码发生变化,以及从mysql数据库迁移到oracle或者其他数据库,需要手动去代码里面再去修改这部分代码,维护起来非常不便。
解决方案:利用配置文件来配置该信息。
-
资源释放,写在finally代码块中
-
可以做一个工具类,将一些重复性代码放入工具类中,直接调用
2 .程序详解
2.1Connection:
Jdbc程序中的Connection,它用于代表数据库的连接(桥梁), Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:
- createStatement():创建向数据库发送sql的statement对象。
- prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
- setAutoCommit(boolean autoCommit):设置事务是否自动提交。
- commit() :在链接上提交事务。
- rollback() :在此链接上回滚事务。
2.2 Statement
Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:
-
executeQuery(String sql) :用于向数据发送查询语句。
-
executeUpdate(String sql):用于向数据库发送insert、update或delete语句
增删改返回的数据是int型代表的是受影响的行数
-
execute(String sql):用于向数据库发送任意sql语句
它返回的是一个boolean类型的数据
2.3 ResultSet
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:
-
获取任意类型的数据
- getObject(int index)
- getObject(string columnName)
-
获取指定类型的数据,(封装数据时方便)例如:
- getString(int index)
- getString(String columnName)
ResultSet还提供了对结果集的游标进行滚动的方法:
- next():移动到下一行
- Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast() :移动到resultSet的最后一行之后。
常用数据类型转换表
- 提问:数据库中列的类型是varchar,获取该列的数据调用什么方法?Int类型呢?bigInt类型呢?Boolean类型?
2.4 释放资源
- Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
- 特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
- 为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
3.数据库注入问题
用户输入的账号密码在代码中是以字符串拼接的方式生成查询语句的,这样用户输入的内容很容易改变我们的原查询代码,这就相当一一个数据库注入问题。
解决方法:
prepareStatement采用一种预先编译好的机制。用户输入的任何内容仅仅会当做普通文本,而不会对里面可能包含的关键字来进行解析。
-
PreparedStatement继承自Statement,可以通过Connection的prepareStatement方法得到。
-
prepareStatement很明显的将sql命令语句与参数分开处理,其执行过程是,首先在sql语句真正执行之前,先把sql命令送到数据库中进行预编译,生成相应 的数据库命令,然后在获取sql中的参数,然后真正执行该sql语句。
-
这样一来,用户输入的参数,只被当做参数而非命令来解析,就可以避免数据库注入这样的问题发生。
-
弊端:单次执行PreparedStatement需要与数据库通信两次,效率,比之于单词执行Statement要低。
JDBC批处理:
业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
默认情况下Mysql的批处理仍然是一条一条执行,需要在url后面添加rewriteBatchedStatements=true参数
实现批处理有两种方式:
- 第一种方式:
相当于使用小推车搬砖,本来是一块一块搬,有了小推车,addbatch将砖放到一个小推车里,executebatch相当于将小推车褪去目的地,clearbatch相当于将小推车里的砖卸了,然后推回目的地。
- Statement.addBatch(sql) :
- 执行批处理SQL语句
- executeBatch()方法:执行批处理命令
- clearBatch()方法:清除批处理命令
优点:可以向数据库发送多条不同的SQL语句。
缺点:- SQL语句没有预编译。
- 当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。
- 实现批处理的第二种方式:
PreparedStatement.addBatch()
注意内存溢出问题 - 优点:与数据库通信次数在批量操作时,PreparedStatment的通信次数远少于Statment。
- 缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
注意:记得在配置文件url 后添加 rewriteBatchedStatements=true,如果不开启rewriteBatchedStatements=true,那么jdbc会把批量插入当做一行行的单条处理,也即没有达到批量插入的效果,和其他代码用&连接 。
例如:
url=jdbc:mysql://localhost:3306/myhome?characterEncoding=utf8username=rootpassword=123456driverClassName=com.mysql.jdbc.Driver
三种批量操作的对比:
import util.JdbcUtil;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
public class BatchCompare {
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
directInsert();
long t1 = System.currentTimeMillis();
statementBatch();
long t2 = System.currentTimeMillis();
prepareStatementBatch();
long t3 = System.currentTimeMillis();
System.out.println("逐条执行的时间为: " + (t1 - beginTime));
System.out.println("statement批处理执行的时间为: " + (t2 - t1));
System.out.println("prepareStatment批处理执行的时间为: " + (t3 - t2));
}
private static void prepareStatementBatch() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
String sql = "insert into user values (null,?,?)";
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < 500; i++) {
//statement.addBatch("insert into user values (null,'" + i + "','" + i + "')");
preparedStatement.setString(1,i + "");
preparedStatement.setString(2,i + "");
preparedStatement.addBatch();
if(i == 249){
//statement.executeBatch();
preparedStatement.executeBatch();
//statement.clearBatch();
preparedStatement.clearBatch();
}
}
//statement.executeBatch();
preparedStatement.executeBatch();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
JdbcUtil.releaseConnection(connection,null,preparedStatement);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void statementBatch() {
Connection connection = null;
Statement statement = null;
try {
connection = JdbcUtil.getConnection();
statement = connection.createStatement();
// statement.addBatch(sql); 相当于将砖放到一个小推车里
// statement.executeBatch(); 推着小推车去目的地
// statement.clearBatch(); 将小推车里面的砖块卸了,再回到目的地
for (int i = 0; i < 500; i++) {
statement.addBatch("insert into user values (null,'" + i + "','" + i + "')");
//statement.addBatch("delete from user where id = 1");
if(i == 249){
statement.executeBatch();
statement.clearBatch();
}
}
statement.executeBatch();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
JdbcUtil.releaseConnection(connection,null,statement);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void directInsert() {
Connection connection = null;
Statement statement = null;
try {
connection = JdbcUtil.getConnection();
statement = connection.createStatement();
for (int i = 0; i < 500; i++) {
statement.executeUpdate("insert into user values (null,'" + i + "','" + i + "')");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
JdbcUtil.releaseConnection(connection,null,statement);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}