使用之前需要在IDEA上连接mysql,然后导入jar包mysql-connector-java-8.0.20.jar
,执行的基本步骤为:
- 注册驱动
- 获取连接对象
- 创建SQL语句
- 创建执行SQL语句的Statement对象
- 执行SQL语句
- 释放资源
6.0版本以上不需要手动加载驱动,可以直接使用
//1. 通过DriverManager来获得数据库连接
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
//2. 创建一个用于执行SQL的Statement对象
Statement statement = connection.createStatement()){ //注意前两步都放在try()中,因为在最后需要释放资源!
//3. 执行SQL语句,并得到结果集
ResultSet set = statement.executeQuery("select * from 表名");
//4. 查看结果
while (set.next()){
...
}
}catch (SQLException e){
e.printStackTrace();
}
//5. 释放资源,try-with-resource语法会自动帮助我们close
URL表示所连接数据库的地址。这里的值是jdbc:mysql://localhost:3306/study
,表示选择本地数据库系统的study数据库,选择其他数据库时就将study
改为其它的数据库。
DriverManager
此类是驱动管理者的意思,通过该类来加载驱动Driver,通过语句Class.forName("com.mysql.cj.jdbc.Driver")
来将Driver类加载进内存。
上述语句执行时,JVM会把该类加载,然后执行一次类内部的静态代码块:
// static code block in Class Driver
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
执行该代码块会通过DriverManager.registerDriver(new Driver());
来注册驱动。DriverManager
注册时会将该Driver
封装成DriverInfo
并加入到DriverManager
类的registeredDrivers
中,这是一个线程安全的列表,表示已经加载的驱动。
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if (driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
Connection对象
通过Mannager对象来获取数据库的连接对象Connection,需要提供三个参数url,user,password。
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
Statement对象和ResultSet对象
Statement是数据库语句对象,通过数据库连接对象来创建一个Statement对象,Statement对象用来执行数据库语句,并将执行结果返回到一个ResultSet中,本质是一个集合。
Statement statement = connection.createStatement(); //
ResultSet set = statement.executeQuery(String sql); // 该方法执行sql语句
// Resultset的使用方法 返回的查询结果是一张表,set里的指针一开始指向表头
while (set.next()) { // 每执行一次next函数,指针就会往下移动一行。
// get函数有两种参数类型:int和string,分别表示所选列的下标和列名
System.out.println(set.getInt(1)); // 选第一列,因第一列为整数,因此使用getInt
System.out.println(set.getString("name")); // 选择列名为 name 的那一列
System.out.println(set.getString(3)); // 选择第三列,为字符串,因此用getString
}
// 批处理,一次想要执行多条数据库语句时,将多条语句合成为一条去执行会更快,数据库只用执行一次。
statement.addBatch("insert into user values ('f', 1234)");
statement.addBatch("insert into user values ('e', 1234)"); //添加每一条批处理语句
statement.executeBatch(); //一起执行
使用Statement对象执行时有个非常严重的问题那就是这里的SQL语句都是静态的,可能会导致SQL注入,出现安全问题。使用PreparedStatement对象可以解决上述问题,该对象是预编译SQL语句的对象,继承自Statement。
SQL注入
演示:
-- 先在数据库中新建user表
CREATE TABLE user(
username varchar(255) NOT NULL,
password varchar(255) NOT NULL,
PRIMARY KEY(username, password)
);
INSERT INTO user values(
'Test', '123');
// 然后使用JDBC尝试连接数据库并登录为user
try( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
Statement statement = connection.createStatement();
Scanner scanner = new Scanner(System.in);
) {
ResultSet set = statement.executeQuery("SELECT * FROM user WHERE username='" + scanner.next() + "' and " + "password='" + scanner.next()+"';");
while (set.next()) {
System.out.println(set.getString("username"));
System.out.println("登录成功");
}
}catch (Exception e) {
e.printStackTrace();
}
正确输入用户名和密码时会正确登录,但是当把密码改为111' or 1=1; --
时,发现也可以正确登录,因为这里的特殊输入“骗过”了数据库系统。这就是SQL注入。
解决SQL注入的方法可以有以下几种:
- 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
- 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
- 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
- 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
- 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
- sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。
使用PreparedStatement
一个PreparedStatement
对象是预编译SQL语句的对象,即在创建时就要把sql语句写好。编写SQL语句时,不使用字符串拼接,而是使用?
占位符来代替变量。然后在真正执行之前再给占位符赋值。
PrepareStatement对象在创建时就要指明要执行的SQL语句类型。然后用?
代替变量,并用setString(int Index, String)
方法来给占位符赋值,最后执行。
try( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
PreparedStatement statement = connection.prepareStatement("SELECT * FROM user WHERE username=? AND password=?");
Scanner scanner = new Scanner(System.in);
) {
statement.setString(1, scanner.nextLine());
statement.setString(2, scanner.nextLine());
ResultSet set = statement.executeQuery();
while (set.next()) {
System.out.println(set.getString("username"));
System.out.println("登录成功");
}
}catch (Exception e) {
e.printStackTrace();
}
再次测试发现原来的输入已经不能“骗过”数据库了。
JDBC管理事务
和数据库管理事务一样的。
try ( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
Statement statement = connection.createStatement();
) {
connection.setAutoCommit(false); // 把连接的自动提交关掉
Savepoint sp = connection.setSavepoint(); // 设置回滚点
statement.executeUpdate("INSERT INTO user VALUES('a', 123)");
statement.executeUpdate("INSERT INTO user VALUES('b', 123)");
statement.executeUpdate("INSERT INTO user VALUES('c', 123)");
connection.rollback(sp); // 回滚到回滚点
connection.commit(); // 手动提交
}catch (Exception e) {
e.printStackTrace();
}
0 条评论