JDBC (Java DataBase Connectivity)
JDBC的概念:
- JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。
- JDBC的作用:可以通过java代码操作数据库
提供者:数据库厂商
是Java提供的、连接不同数据库的驱动
DriverManager类
提供者:Sun公司
作用:一依据数据库的不同,来管理JDBC驱动
获取驱动:
在JDBC的Driver中有一行源代码如下:
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
调用了Java.sql包下的DriverManager的registerDriver方法,注册了一个驱动
我们也可以使用Java的反射来将Driver来引入项目:
Class.forName("com.mysql.cj.jdbc.Driver");
Connection接口
是Java程序连接数据库程序的桥梁
使用Connection来连接到数据库实例:
String url = "jdbc:mysql://127.0.0.1:3306/emp_management?" +
"useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8";
String username = "root";
String password = "159357";
Connection conn = null;
try {
/*
1、加载驱动类,"com.mysql.cj.jdbc.Driver"类的具体路径
也称之为类的全限定名
*/
Class.forName("com.mysql.cj.jdbc.Driver");
/*
2、创建连接,需要3个参数
参数1:数据库系统的地址:端口:数据库名称
参数2:登录数据库系统的用户名
参数3:登录数据库系统的密码
*/
conn = DriverManager.getConnection(url,username,password);
System.out.println("正常连接到数据库" + conn);
/*
3、操作数据
*/
/*stmt = conn.createStatement();
stmt.executeUpdate(sql);*/
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
try {
conn.close();
System.out.println("连接正常关闭");
} catch (SQLException e) {
e.printStackTrace();
}
}
url中各项参数的解释:
参数名称 | 参数含义 |
---|---|
jdbc:mysql | 固定写法,说明连接的数据库是mysql |
😕/127.0.0.1:3306 | 表示连接到该ip地址上的数据库,本机可以写localhost和127.0.0.1 |
/emp_management | 数据库名称,表示你要连接哪个数据库 |
?useSSL=false | 因为MySQL8.0之后默认开启SSL安全协议,需要一些安全证书才能访问,因此我们需要手动关闭 |
&serverTimezone=UTC | |
&useUnicode=true | 使用Unicode字符集 |
&characterEncoding=UTF8 | 设置字符编码,如果有中文,那么可以解决中文乱码问题 |
Statement接口
使用流程:
- 因为Statement是基于Connection对象的,因此我们需要获取数据库连接对象,连接数据库
- 使用Connection对象的createStatement()方法创建Statement对象实例
- 然后我们需要一个等待执行的SQl语句,因为需要多次拼接,所以使用StringBuffer效率更高
- 使用Statement对象的executeUpdate()、executeQuery()方法来执行我们拼接好的SQL语句、
- 关闭Statement对象,释放资源
使用实例:像admin表中插入一条数据
String name = null;
String pwd = null;
Scanner sc = new Scanner(System.in);
Connection conn = null;
Statement stmt = null;
try {
/*
1、加载驱动类,"com.mysql.cj.jdbc.Driver"类的具体路径
也称之为类的全限定名
*/
Class.forName("com.mysql.cj.jdbc.Driver");
/*
2、创建连接,需要3个参数
参数1:数据库系统的地址:端口:数据库名称
参数2:登录数据库系统的用户名
参数3:登录数据库系统的密码
*/
conn = DriverManager.getConnection(url, username, password);
System.out.println("正常连接到数据库" + conn);
//信息录入
System.out.print("请输入管理员姓名:");
name = sc.next();
System.out.print("请输入管理员密码:");
pwd = sc.next();
//拼接sql语句,因为是单线程,所以使用StringBuilder来提升性能
StringBuilder sql = new StringBuilder("insert into admin values(null,'");
sql.append(name);
sql.append("','");
sql.append(pwd);
sql.append("')");
/*
3、操作数据
*/
stmt = conn.createStatement();
stmt.executeUpdate(sql.toString());
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
try {
conn.close();
stmt.close();
System.out.println("连接正常关闭");
} catch (SQLException e) {
e.printStackTrace();
}
}
ResultSet接口
进行查询操作时使用
Statement对象进行executeQuery()返回的是一个ResultSet结果集
connection = DriverManager.getConnection(url, username, password);
statement = connection.createStatement();
resultset = statement.executeQuery(sql.toString());
游标机制:
实际上就是一个指针,最开始指向第0行(第0行没有任何数据),遍历的时候使用next()读取到一行数据,并将指针向下移动一行;
我们读取数据行中的数据,有两种方法:
第一种是索引:resultSet.getInt(1) 取第一列的数据
第二中是凭借列名来获取数据:resultSet.getString(“gradeName”); 获取此行列名为gradeName的数据
get后面的Int表示我们获取的数据类型是Int
同理有getString()、getDouble()等
使用实例:
while (rs.next()) {
System.out.print(rs.getInt(1) + "\t");
System.out.print(rs.getString("gradeName") + "\t");
System.out.println(rs.getString(3));
}
PreparedStatement接口
是Statement接口的子接口
- 能够预编译好我们的SQL语句,可以防止SQl注入攻击等
- 具有更好的可读性和可维护性
- 能够提升SQl语句执行的效率
SQL注入攻击
我们来看一个Login_Check的例子
StringBuffer sql = new StringBuffer("select count(*) from admin where username='");
sql.append(username);
sql.append("' and userpwd = '");
sql.append(password);
sql.append("'");
如果我们输入的username 为
' or 1=1 --
那么完整的SQL语句就是
select count(*) from admin where username='' or 1=1 -- and userpwd ='password';
也就是说username=‘’ or 1=1恒成立,并且后面的内容被注释掉了,账号密码错误也能登录成功,这也就是SQL注入攻击
为什么PreparedStatement能防止sql注入呢?
因为sql语句是预编译的,而且语句中使用了占位符,规定了sql语句的结构。
用户可以设置"?"的值,但是不能改变sql语句的结构,
那么preparedStatement怎么使用呢?
String sql = "select count(*) from admin where username=? and userpwd=?";
prstmt = conn.prepareStatement(sql);
prstmt.setObject(1, username);
prstmt.setObject(2, password);
rs = prstmt.executeQuery();
第二第三句是对SQL中的问号赋值,因为不知道我们赋的值是什么类型的,所以统一用setObject()来赋值
因为赋值完之后preparedStatement就会进行预编译,所以在executeQuery()中不必传入参数
开发中的小技巧
我们在开发项目时,
设计表的时候一般会在最后添加一列状态列,通常叫active或者state,
如果碰到需要删除表信息的情况,一般不能直接delete,
而是将要删除的数据行状态值设置为隐藏(从1变成0)
这种删除方式称为逻辑删除