想要学习mybatis的相关知识,学习之前复习了下JDBC的相关知识
发现自己竟然连如何实现JDBC连接都不知道了,真的是少壮用CV(粘贴复制),老大徒伤悲
总结一下,JDBC连接分为三步:
简单的连接和查询示例如下:
import java.sql.*;
public class Test {
private static final String DRIVER = "com.mysql.jdbc.Driver";
// 指定编码格式,解决无法匹配中文字符的问题
private static final String URL = "jdbc:mysql://localhost:3306/lucy?useUnicode=TRUE&characterEncoding=UTF-8";
private static final String USER = "root";
private static final String PASSWORD = "123456";
private static Connection conn = null;
private static Statement stmt = null;
private static ResultSet rs = null;
private static PreparedStatement ptmt = null;
public static void main(String[] args) {
try {
// 1. 加载驱动
Class.forName(DRIVER);
// 2. 创建数据库连接
conn = DriverManager.getConnection(URL, USER, PASSWORD);
// 3. 操作数据库,实现增删改查
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from stu");
// 遍历查询结果
while (rs.next()) {
StringBuilder sb = new StringBuilder("[id=");
sb.append(rs.getLong("id"));
sb.append(", name=" + rs.getString("name"));
sb.append(", age=" + rs.getInt("age"));
sb.append(", class_id=" + rs.getLong("class_id"));
sb.append("]");
System.out.println(sb.toString());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally { // 关闭连接
try {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (ptmt != null) {
ptmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
stu表的建表语句见博客:数据库中的四大join & 笛卡尔乘积(以MySQL为例)
新需求:这是一个简单的学生信息管理系统,需要通过学生姓名查询学生信息。
详细设计:编写一个方法,入参为学生姓名,将入参拼接成完整的SQL语句
代码实现:
public static void queryWithParam(String name) throws SQLException {
String sql = "select * from stu where name='" + name + "'";
rs = stmt.executeQuery(sql);
// 打印查询结果
while (rs.next()) {
StringBuilder sb = new StringBuilder("[id=");
sb.append(rs.getLong("id"));
sb.append(", name=" + rs.getString("name"));
sb.append(", age=" + rs.getInt("age"));
sb.append(", class_id=" + rs.getLong("class_id"));
sb.append("]");
System.out.println(sb.toString());
}
}
如果用户传入的参数为lucy
,此时SQL字符串为select * from stu where name='lucy'
,能查询到想要的数据
如果这是一个急性子的用户,他不仅想要知道lucy
的信息,还想要知道郭麒麟
的信息,这时传入的参数为:lucy' or name='郭麒麟
拼接出来的SQL语句为select * from stu where name='lucy' or name='郭麒麟'
,查询出来的学生信息就不止lucy
一条信息了
由此可见,这样的字符串拼接方式存在SQL注入的风险
'
字符进行转义\'
,这样就保证其整体性lucy\' or name=\'郭麒麟
简单的转义在字符串拼接时,会自动取消转义:select * from stu where name='lucy' or name='郭麒麟'
要避免SQL注入攻击,一个办法是针对所有字符串参数进行转义,但是转义很麻烦,而且需要在任何使用SQL的地方增加转义代码。
可以使用PreparedStatement
来实现数据库操作,它的特点是:使用占位符?
表示SQL语句中的参数,通过set
方法为SQL语句传入参数
简单示例如下:
public static void queryPrepare(String name) throws SQLException {
// 预编译SQL,避免
ptmt = conn.prepareStatement("select * from stu where name=?");
ptmt.setString(1, name);
// 打印预编译后的SQL语句
System.out.println(ptmt.toString());
rs = ptmt.executeQuery();
while (rs.next()) {
StringBuilder sb = new StringBuilder("[id=");
sb.append(rs.getLong("id"));
sb.append(", name=" + rs.getString("name"));
sb.append(", age=" + rs.getInt("age"));
sb.append(", class_id=" + rs.getLong("class_id"));
sb.append("]");
System.out.println(sb.toString());
}
}
这里,我们仍然传入之前成功发生SQL注入的参数lucy' or name='郭麒麟
,发现打印出来的SQL语句为:
select * from stu where name='lucy\' or name=\'郭麒麟'
神奇之处:预编译自动将SQL语句进行了转义,使得传入的参数成为了一个整体
也就是说,这里的查询条件变成了lucy\' or name=\'郭麒麟
,SQL语句无法被截断了
使用PreparedStatement
的优点:
PreparedStatement
可以有效防止sql注入,而Statment
不能防止sql注入。PreparedStatement
可以使用预编译的sql,而Statment
只能使用静态的sqlPreparedStatement
可以使用sql缓存区,效率比Statment
高