1 JDBC
JDBC是Java访问数据库的标准规范,是一种用于执行SQL语句的API,可以为任何关系型数据库提供统一的访问,由一组用Java语言编写的类和接口组成.
注意JDBC只是个接口,数据库驱动是接口的实现,而各个数据库的驱动都是由各个数据库厂商负责提供,用来连接自己公司的数据库。
2 JDBC开发
先准备数据库的数据,具体代码如下:
create table jdbc_user(
id int primary key auto_increment,
username varchar(50),
password varchar(50),
birthday date
);
insert into jdbc_user values (null,'tom','123456','1991/12/24'),
(null,'jack','root','1999/10/24'),
(null,'admin','123','2000/12/24');
将MySql对应的驱动包添加到jar包库文件夹中,下面开始正式的JDBC代码编写工作:
1 注册驱动
JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver,所以我们选择这个实现类
加载驱动的语法格式是:Class.forName(数据库驱动实现类),实例代码是:
Class.forName(com.mysql.jdbc.Drive);
注意:从JDBC3开始,可以不用注册驱动而直接使用,Class.forName这句话可以省略掉。
2 获取连接
Connection接口代表连接对象,具体的实现类有厂商实现,使用DriverManager类的静态方法getConnection可以获取数据库的连接,getConnection方法介绍如下:
方法声明 | 介绍 |
---|---|
Connection getConnection(String url,String user,String password) | 根据连接字符串,用户名和密码获取数据库连接对象 |
其中URL数据库连接字符串写法如下:jdbc:mysql://localhost:3306/db?characterEncoding=utf-8,其中第一部分是协议jdbc,这是固定的;第二部分是子协议,就是数据库名称,这里是MysSql数据库;第三部分是数据库厂商规定的,而MysSql要求是有数据库服务器的IP地址、端口号以及以及要使用的数据库名称组成。
3 获取语句执行平台
通过Connection的createStatement()方法获取sql语句执行对象,关于Connection接口的方法介绍如下:Statement createStatement() 创建SQL语句执行对象。
Statement类:代表一条语句对象,用于发送SQL语句给服务器,用于执行静态SQL语句并返回生成的结果对象,具体方法介绍如下。
方法声明 | 介绍 |
---|---|
int excuteUpdate(String sql) | 执行insert,update,delete语句,返回受影响的行数 |
ResultSet executeQuery(String sql) | 执行select语句,返回ResultSet结果集对象 |
4 处理结果集
只有当执行查询语句的时候,才用处理结果集,其中ResultSet接口是用来封装数据库查询的结果集,对结果集进行遍历,取出每一条记录,关于ResultSet接口的方法介绍如下:
方法声明 | 功能介绍 |
---|---|
boolean next() | 游标指向下一行,如果有下一条记录,就返回true,否则就返回false |
Xxx getXxx(String or int ) | 当参数是字符串时,通过列名获取值 当参数是整数时,通过列号来获取值 |
5 释放资源
在JDBC中需要我们是释放的资源有:ResultSet结果集、Statement语句、Connection连接,释放原则就是先开的后关,后开的先关,一般建议将释放资源的语句放在finally块中。
一般来说关闭顺序是:ResultSet结果集 -> Statement语句 -> Connection连接
关于JDBC访问数据库的步骤:
- 获取驱动(可以省略)
- 获取连接
- 获取Statement语句平台对象
- 处理结果集(只在执行查询时处理)
- 释放资源
3 JDBC实现增删改查
当一个功能经常需要使用的时候,我们可以将这个功能封装成一个工具类,可以在不同的地方重用。由于获取数据库连接的操作在以后的增删改查操作中都会使用到,因此我们可以封装一个JDBCUtils工具类,实现数据库连接的创建和资源的释放,具体代码如下:
package com.lagou.task01.utils;
import java.sql.*;
public class JDBCUtils {
public static final String DRIVERNAME="com.mysql.jdbc.Driver";
public static final String URL="jdbc:mysql://localhost:3306/db?characterEncoding=utf-8";
public static final String USER="root";
public static final String PASSWORD="root";
static {
try {
Class.forName(DRIVERNAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
return connection;
} catch (SQLException throwables) {
throwables.printStackTrace();
return null;
}
}
public static void close(Connection cn, Statement st) {
if (null != cn && null != st) {
try {
st.close();
cn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Connection cn,Statement st,ResultSet rs){
if(null!=rs){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
close(cn,st);
}
}
使用JDBC对数据库的数据进行增删改查操作,具体代码如下:
package com.lagou.task01.jdbc01;
import com.lagou.task01.utils.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;
public class JDBCTest {
@Test
public void testInsert() throws Exception {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql="insert into jdbc_user values(null ,'Eric','qwer','1998/01/06')";
int rs=statement.executeUpdate(sql);
System.out.println(rs);
JDBCUtils.close(connection,statement);
}
@Test
public void testUpdate() throws Exception {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql="update jdbc_user set username='张百万' where id=5";
statement.executeUpdate(sql);
JDBCUtils.close(connection,statement);
}
@Test
public void testDelete() throws Exception{
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql="delete from jdbc_user where id=5";
statement.executeUpdate(sql);
JDBCUtils.close(connection,statement);
}
@Test
public void testQuery() throws Exception{
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
String sql="select * from jdbc_user";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
int id=resultSet.getInt("id");
String username=resultSet.getString("username");
String password=resultSet.getString("password");
Date birthday=resultSet.getDate("birthday");
System.out.println(id+" "+username+" "+password+" "+birthday);
}
JDBCUtils.close(connection,statement,resultSet);
}
}
4 SQL注入问题
先看一句SQL代码
select * from jdbc_user where username='tom' and password='222' or '1'='1';
可以看到只要执行上面的代码,不管用户名和密码是否正确,都可以从数据库中查询出数据,这正是SQL注入所使用的方法。
所谓SQL注入是用户输入的内容作为SQL语句的一部分,改变了原有SQL的真正意义,这就是SQL注入。
解决方案:很简单我们只需要不让用户输入的内容和我们的SQL语句进行简单的字符串拼接,这就需要使用到预处理对象PreparedStatement。
PreparedStatement是Statement接口的子接口,是一个预编译的SQL语句对象,而所谓的预编译就是指SQL语句被预编译,并存储在PreparedStatement对象中,然后可以使用此对象多次高效的执行该语句。
PreparedStatement对象的特点:
- 因为有预先编译的功能,提高了SQL语句执行的效率
- 可以有效的防止SQL注入的问题,安全性更高
PrepareStatement接口的常用方法:
方法声明 | 功能介绍 |
---|---|
PreparedStatement prepareStatement(String sql) | 指定预编译的SQL语句,创建语句对象 |
int executeUpdate() | 执行insert、update、delete语句 |
ResultSet executeQuery() | 执行select语句,返回查询到的结果集 |
使用PrepareStatement的实例代码:
@Test
public void PrepareStatementTest()throws Exception{
Connection connection = JDBCUtils.getConnection();
String sql="select * from jdbc_user where username=? and password=? ";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"admin");
preparedStatement.setString(2,"123");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id=resultSet.getInt("id");
String username=resultSet.getString("username");
String password=resultSet.getString("password");
Date birthday=resultSet.getDate("birthday");
System.out.println(id+" "+username+" "+password+" "+birthday);
}
JDBCUtils.close(connection,preparedStatement,resultSet);
}
可以看到PreparedStatement和Statement的区别是:
- Statement用于执行静态的SQL语句,在执行时,必须制定一个事先准备好的SQL语句
- PreparedStatement是预编译的SQL语句对象,语句中可以包含动态参数"?",在执行时可以为"?"占位符动态设置参数值
- PreparedStatement可以减少编译次数,从而提高数据库性能
5 JDBC控制事务
在MySQL中我们可以通过命令的方式操作事务,当然在JDBC中我们也可以通过代码的方式操作事务,使用Connection中的方法实现对事务的管理,具体方法介绍如下:
方法声明 | 功能介绍 |
---|---|
void setAutoCommit(boolean auto) | 参数是true时时自动提交事务,否则就关闭自动提交,相当于开启事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
JDBC控制事务的步骤是:
- 获取连接
- 开启事务
- 获取到PreparedStatement,执行SQL语句
- 正常情况下提交事务
- 出现异常就回滚
- 关闭资源
代码示例:
@Test
public void TransactionTest(){
Connection connection=null;
PreparedStatement ps=null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);
String sql="update jdbc_user set username=? where id=?";
ps=connection.prepareStatement(sql);
ps.setString(1,"王大锤");
ps.setInt(2,2);
ps.executeUpdate();
connection.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtils.close(connection,ps);
}
}