一、jdbc的基础知识
初步入门jdbc,及一些遇到的问题
1、什么是jdbc
java database Connector java与数据库的连接器
一套接口,是java代码与数据库的桥梁
2、重要接口和类
1)java.sql.Connection (连接接口–代表了java代码与数据库服务器之间的连接)
2)java.sql.Statement (语句接口–代表了语句对象,可以用来执行各种sql)
3)java.sql.ResultSet (结果集接口–代表查询结果)
4)DriverManager (驱动管理器) 辅助类 – 获取Connection连接
5)java.sql.Driver (驱动接口)
6)java.sql.SQLException 检查异常, 需要处理
jdbc 实现–由各个数据库厂商提供
3、jdbc的开发步骤
- 加载驱动
Class.forName("驱动类名");
- 创建数据库连接
Connection conn = DriverManager.getConnection(连接字符串, 用户名, 密码);
一些个人理解:
对于以下例题中的参数,jdbc:mysql://localhost:3306/test3?serverTimezone=GMT%2B8&useSSL=false,3306是数据库安装默认端口,如果安装时你修改了就不是这个值。test3是所选用的数据库名称。serverTimezone=GMT%2B8是指你处于东八区,GMT指的是格林威治标准时间,%2B代表+的意思,因为HTML在get请求方式下+可能会被处理为空,为避免出现这种情况所以使用转义字符对其转义。至于useSSL=false就是使用JDBC跟你的数据库连接的时候,你的JDBC版本与MySQL版本不兼容,MySQL的版本更高一些,在连接语句后加上在url中加上“userSSL=true”或“userSSL=false”,就可以连接到更高版本数据库了。
对于“mysql-connector-java 8”以上版本。与老版本有一些区别,
JDBC driver 应由“com.mysql.jdbc.Driver”改为“com.mysql.cj.jdbc.Driver”
- 创建Statment(Preparedstatement)对象
Statement stmt = conn.createStatement();
执行增删改
int rows = executeUpdate(sql语句);
执行查询
ResultSet rs = executeQuery(sql语句);
- 关闭资源
新增示例代码:
public static void insert() throws Exception{
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 创建数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test3?serverTimezone=GMT%2B8&useSSL=false", "root" "root");
// 3. 创建Statment对象,执行sql
Statement stmt = conn.createStatement();
// 返回值 int类型 代表影响的记录行数
String sql = "insert into student(sid,sname,birthday,sex) values (null,'老炮儿','1999-1-1','男')";
int x = stmt.executeUpdate(sql);
System.out.println("影响的记录行数 x = " + x);
// 4. 关闭资源 先打开的后关闭
stmt.close(); // 关闭statement对象
conn.close(); // 关闭数据库连接
}
查询示例代码:
public static void select() throws Exception {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 创建数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test3?serverTimezone=GMT%2B8&useSSL=false", "root" "root");
// 3. 创建Statment对象,执行sql
Statement stmt = conn.createStatement();
// executeQuery(sql) 执行sql的方法
// 返回值ResultSet类型 代表影响的结果集
String sql = "select sid,sname,birthday,sex from student";
ResultSet rs = stmt.executeQuery(sql);
// next()方法用来移动到下一行记录 getXXX方法用来获取某列数据
while (rs.next()) {
int sid = rs.getInt(1);
String sname = rs.getString(2);
Date birthday = rs.getDate(3);
String sex = rs.getString(4);
System.out.println(sid + " " + sname + " " + birthday + " " + sex);
}
// 4. 关闭资源 先打开的后关闭
rs.close(); // 关闭ResultSet
stmt.close(); // 关闭Statement对象
conn.close(); // 关闭数据库连接
}
注意
问题1:MySQL Connector/J 8.0.12 驱动连接时需要添加serverTimezone=GMT%2B8
参数,否则会出现异常:java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’,另外GMT%2B8
也必须加,否则会出现连接方和数据库时区不一致问题
问题2:在连接过程中一旦发生异常:java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed 需要添加allowPublicKeyRetrieval=true
参数,此异常一种可能的发生情况是MySQL服务重启后,立刻用jdbc连接时
问题3:WARN: Establishing SSL connection without server’s identity verification is not recommended 要消除这个警告信息,需要加入参数useSSL=false
4、获取自增列的值
Statement stmt = conn.createStatement();
String sql = "insert into student(sid,sname,birthday,sex) values (null,'老炮儿','1999-1-1','男')";
stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
rs.next();
System.out.println(rs.getInt(1));
5、事务控制
6、注入攻击问题与解决方法
数据库表:
create table xx_user (
username varchar(20) primary key,
password varchar(20) not null
);
insert into xx_user values('wang', '123');
登录方法:
// 登录方法,如果有用户名和密码其中之一不正确,返回false表示登录失败
/**
*
* @param username 用户输入的用户名
* @param password 用户输入的密码
* @return
*/
public boolean login (String username, String password) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe", "scott", "tiger");
stmt = conn.createStatement();
String sql = "select * from xx_user where username = '"+username+"' and password = '"+password+"'";
System.out.println(sql);
rs = stmt.executeQuery(sql);
if(rs.next()) {
return true;
} else {
return false;
}
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException("sql执行失败", e);
} finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
一个注入攻击问题:
用户名和密码都没有输入正确,但返回true的例子
boolean success = dao.login("laowang", "aaa' or '1'='1");
System.out.println("登录" + success);
解决注入攻击的一种手段:
在PrepareStatement
对应的sql语句中,可以使用?
代表一个未知的值。
?
只能代表值,不能是关键字,表名,列名- 调用的方法根据
?
的实际类型而定,例如值是int 调用setInt方法 ?
的对应的set方法下标从1
开始
// 1) 创建 PreparedStatement
PreparedStatement stmt = conn.prepareStatement("select * from xx_user where username = ? and password = ? ");
// 2) 给?占位符赋值
stmt.setString(1, "laowang");
stmt.setString(2, "aaa' or '1'='1"); // 会将整个值当做一个整体,把or当做了值而不是关键字
// 3) 运行sql
ResultSet rs = stmt.executeQuery(); // 会将sql语句以及通过set方法设置的参数值,一起发送给数据库服务器
if(rs.next()) {
System.out.println("查询到了");
} else {
System.out.println("没查询到");
}