封装
1. 在src目录下创建一个db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy? useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
2. 下新建一个 utils 包,新建一个类 JdbcUtils
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null; static{
try{
//读取db.properties文件中的数据库连接信息
//此处涉及反射的类加载器和map子接口properties类的知识
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
//获取数据库连接驱动
driver = prop.getProperty("driver");
//获取数据库连接URL地址
url = prop.getProperty("url");
//获取数据库连接用户名
username = prop.getProperty("username");
//获取数据库连接密码
password = prop.getProperty("password");
//加载数据库驱动
Class.forName(driver);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
// 获取数据库连接对象
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
}
// 释放资源,要释放的资源包括Connection数据库连接对象,存储查询结果的ResultSet对象
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
} }if(conn!=null){
try{
//关闭Connection数据库连接对象
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
3.封装好后,插入一条数据(删改查同理)
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
//获取一个数据库连接
conn = JdbcUtils.getConnection();
//通过conn对象获取负责执行SQL命令的Statement对象
st = conn.createStatement();
//要执行的SQL命令
String sql = "insert into users(id,name,password,email,birthday) " + "values(4,'kuangshen','123','24736743@qq.com','2020-01- 01')";
//执行插入操作,executeUpdate方法返回成功的条数
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
//SQL执行完成之后释放相关资源
JdbcUtils.release(conn, st, rs);
}
}
}
SQL 注入问题
通过巧妙的技巧来拼接字符串,造成SQL短路,从而获取数据库数据。
public static void main(String[] args) {
login("zhangsan","123456"); // 正常登陆
login("'or '1=1","'or '1=1"); // SQL 注入 }
//查询语句
String sql = "select * from users where name='"+username+"' and password='"+password+"'";
//注入示例,条件变为永真
select * from users where name='' or '1=1' and password = ''or '1=1' "
PreparedStatement对象
-
PreperedStatement是Statement的子类,它的实例对象可以通过调用Connection.preparedStatement()方法获得。
-
PreperedStatement可以避免SQL注入的问题。
-
PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。
-
PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
一、 插入数据
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
//获取一个数据库连接
conn = JdbcUtils.getConnection();
//要执行的SQL命令,SQL中的参数使用?作为占位符
String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
//通过conn对象获取负责执行SQL命令的prepareStatement对象
st = conn.prepareStatement(sql);
//为SQL语句中的参数赋值,注意,索引是从1开始的
st.setInt(1, 4);
//id是int类型的
st.setString(2, "ha");
//name是varchar(字符串类型)
st.setString(3, "123");
//password是varchar(字符串类型)
st.setString(4, "234@qq.com");
//email是varchar(字符串类型)
st.setDate(5, new java.sql.Date(new Date().getTime()));
//birthday是date类型
//执行插入操作,executeUpdate方法返回成功的条数
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}catch (Exception e) {
e.printStackTrace();
}finally{
//SQL执行完成之后释放相关资源
JdbcUtils.release(conn, st, rs);
}
}
}
避免注入原理:执行的时候参数会用引号包起来,并把参数中的引号作为转义字符,从而避免了参数也作为条件的一部分。