JDBC简介:
JDBC全称:Java Data Base Connectivity(Java数据库连接),它主要由接口组成。
导入两个包 java.sql;javax.sql;
java.sql.*; 是核心包里的
java.sql.*是jdbc2.0之前的东西
javax.sql.*; 是扩展包中的
javax.sql.*包括了jdbc3.0的特性
//excute [ˈeksɪkju:t] 执行; 完成
数据库的访问过程
- 客户端与MySQL服务器建立连接
- 客户端向MySQL服务器建立数据库请求
- MySQL服务器处理客户端请求,并将结果返回给客户端
- 客户端接受MySQL服务器的响应,并且按照自己的业务逻辑做响应处理
释放相关资源
具体操作过程
- 注册驱动
Sun公司定义了JDBC的一些接口,用户要想操作数据库,需要先把数据库的驱动,也就是JDBC的实现类拿到程序里来,这个操作称为注册驱动。Sun公司定义了DriverManager类用于完成驱动程序的注册:
DriverManager.registerDrive(new com.jdbc.mysql.Driver()); - 驱动注册后就可以创建与数据库的连接了
Connection connection=DriverManager.getConnection(); - 获得连接后,就可以在此连接上创建一条SQL语句,并发送给数据库
Statement st=connection.createStatement();
ResultSet set=st.excute(“slelct * from user”)
Connection详解:
- JDBC中的Connection用于代表数据库的连接(桥梁),Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过Connection对象完成的,这个对象的常用方法:
createStatement():创建向数据库发送sql的statement对象
prepareStatement():创建向数据库发送预编译sql的PrepareStatement对象
setAutoCommit(boolean autoCommit):设置事务是否自动提交
commit():在连接上提交事务
rollback():在此连接上回滚事务
Statement详解:
- JDBC程序中的Statement对象用于向数据库发送SQL语句
Statement对象常用方法:
executeQuery(String sql):用于向数据库发送查询语句
executeUpdate(String sql):用于向数据库发送insert、update或者delete语句
excute(String sql):用于向数据库发送任意sql语句
ResultSet详解:
- JDBC程序汇总的ResultSet用于代表SQL语句的执行结果。Result封装执行结果时,采用的类似于表格的方式。Result对象维护了一个指向表格数据行的游标,游标在第一行之前,调用Result.next()方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
- ResultSet对象提供的都是用于获取数据的get方法
getObject() getString() getDate()等
释放资源:
JDBC程序运行完后,切记要释放程序在运行过程中创建的那些与数据库交互的对象,这些对象通常是ResultSet、Statement、Connection对象
Connection对象的使用原则是尽量晚创建,尽量早的释放
为了确保资源释放代码能够运行,资源释放代码也一定要放在finally语句中
数据库的CRUD操作:
DriverManager.registerDrive(new com.jdbc.mysql.Driver());
Connection connection=DriverManager.getConnection();
Statement st=connection.createStatement();
//create
String sql="delete from user where id=1";
int num=st.excuteUpdate(sql);
if(num>0){
System.out.println("插入成功");
//update
String st=connection.createStatement();
String sql="update user set name="" where name=" " ";
int num=st.excuteUpdate(sql);
if(num>0){
System.out.println("修改成功");
}
//delete
String st=connection.createStatement();
String sql="delete from user where id=1";
int num=st.excuteUpdate(sql);
if(num>0){
System.out.println("删除成功");
}
//read
String st=connection.createStatement();
String sql="delete from user where id=1";
int num=st.excuteQuery(sql);
while(rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法
//映射到java对象中
}
数据库注入问题:
我们的代码中,最终拼接而成的发给数据库执行的SQL语句是:
String sql = “select * from t_user where name =’”+ name +”’ and password = ‘”+ password +”’”;
如果用户在name中输入了haha or 1=1– 这样就发生了数据注入问题
- 仔细分析一下,数据库注入成功的根本原因是,我们把sql语句中的参数(用户的输入)和sql命令拼接成了一个sql语句,因为一个sql语句中既可以有sql的命令又可以有参数,因此,用户的输入也可以被当做sql的语句来解析执行。
- 那么,既然知道了sql注入成功的原因,我们就反其道而行之,不让用户输入的参数被当做sql命令解析,而是只把它当做普通字符串来解析。
- 由此,java中引入了prepareStatement,利用preparestatement来防止sql注入的核心思想就是,不把用户的输入当做sql命令来解析和执行。
PreparedStatement
- PreparedStatement继承自Statement,可以通过Connection的prepareStatement方法得到
- prepareStatement很明显的将sql命令语句与参数分开处理,其执行过程是,首先在sql语句真正执行之前,先把sql命令送到数据库中进行预编译,生成相应 的数据库命令,然后在获取sql中的参数,然后真正执行该sql语句。
- 这样一来,用户输入的参数,只被当做参数而非命令来解析,就可以避免数据库注入这样的问题发生。
- 显然,这样一来,单次执行PreparedStatement需要与数据库通信两次,效率,比之于单词执行Statement要低。
使用JDBC进行批处理
- 业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
- 如何实现批处理:
方法一Statement.addBatch(sql)
executeBatch():执行批处理命令
clearBatch():清楚批处理命令
Connection connection=null;
Statement statement=null;
ResultSet resultset=null;
try{
connection=JdbcUtil.getConnection();
String sql1="insert into user(name,password,email,birthday)"
values('kkk','123','abc@sina.com','1978-08-08');
String sql2="update user set password='123456' where id=3";
statement=connection.createStatement();
//把SQL语句加入到批命令中
statement.addBatch(sql1);
statement.addBathc(sql2);
//把SQL语句提交给MySQL
statement.excuteBatch();
}finally{
//释放资源
JdbcUtil.free(connection,statement,resultset)
}
- 采用Statement.addBatch(sql)方式实现批处理
优点:可以向数据库发送多条不同的SQl语句;
缺点:SQL语句没有预编译(PreparedStatement),容易出现注入现象。
同时向数据库发送多条语句相同但仅参数不同的SQL语句时,需要重复写上很多条SQL语句
方法二PreparedStatement.addBatch()
注意内存溢出问题
Connection connection = JdbcUtil.getConnection();
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
Statement statement = connection.prepareStatement(sql);
for(int i=0;i<50000;i++){
statement .setString(1, "haha" + i);
statement .setString(2, "haha" + i);
statement .setString(3, "haha" + i + "@gamil.com");
statement .setDate(4,new Date(1999, 9, 9));
statement .addBatch();
if(i%1000==0){
statement .executeBatch();
statement .clearBatch();
}
}
statement .executeBatch();
采用PreparedStatement.addBatch()实现批处理
优点:对于sql语句,PreparedStatement在服务端的编译,优化次数要远小于Statement。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。对于Statement对象,我们已经知道是封装SQL语句并发送给数据库执行的功能,但是实际开发中,这个功能我们更经常用的是Statement类的子类PreparedStatement类的对象来实现,而不是采用Statement对象。
Statement和PreparedStatement的关系与区别在于:
① PreparedStatement类是Statement类的子类,拥有更多强大的功能。
② PreparedStatement类可以防止SQL注入攻击的问题,后面会说到。
③ PreparedStatement会对SQL语句进行预编译,以减轻数据库服务器的压力,而Statement则无法做到。