JDBC学习
Connection conn = null;
PreparedStatement ps = null;
try{
//1.注册驱动
Class.forName("con.mysql.jdbc.Driver");
//2.获取连接
/*
jdbc:mysql://127.0.0.1:3306/bjpowernode
jdbc:mysql:// 协议
127.0.0.1 IP地址
3306 mysql数据库端口号
bjpowernode 具体的数据库
说明:localhost和127.0.0.1都是本机IP地址。
什么是通信协议,有什么用?
通信协议是通信之前就提前定好的数据传送格式。
数据包具体怎么传数据,格式提前定好的。
*/
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root";
String passoword = "123456";
conn = DriverManager.getConnection(url,user,password);
//3.获取数据库操作对象(Statement专门执行sql语句的)
//4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
ps = conn.prepareStatement();
ps.setString(1,xxx);
ps.setString(2,xxx);
ps.setString(3,xxx);
//专门执行DML语句的(insert delete update)
//返回值是“影响数据库中的记录条数”
int count = ps.executeUpdate();
//5.处理查询结果集,如果是select语句
}catch(SQLException e){
e.printStackTrace();
}finally{
//6.释放资源
//为了保证资源一定释放,在finally语句块中关闭资源
//并且要遵循从小到大依次关闭
//分别对其try...catch
try{
if(ps != null){
ps.close();
}}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn != null){
conn.close();
}}catch(SQLException e){
e.printStackTrace();
}
}
JDBC中的sql语句不需要提供分号
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,user,password);
stmt = conn.createStatement();
stmt.executeUpdate(sql);
//DriverManager 驱动管理器
//Statement 报告
//executeUpdate 执行更新
//注册驱动的另一种方式(这种方式常用)
//以下方法不需要接收返回值,因为我们指向用它的类加载动作
Class.forName("com.mysql.jdbc.Driver");
/*
处理查询结果集
*/
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/bjpowernode";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url,user,password);
stmt = conn.createStatement();
String sql = "select empno,ename,sal from emp";
//int executeUpdate (insert/delete/update)
//ResultSet executeQuery(select)
rs = stmt.executeQuery(sql);//专门执行DQL语句的方法
//5.处理查询结果集,如果是select语句
while(rs.next()){
//查询结果最后的列名(要注意重命名的情况)
int empno = rs.getInt("empno");
String ename = rs.getString("ename");
double sal = rs.getDouble("sal");
System.out.println(empno + "," + ename +"," + sal);
}
}catch(SQLException e){
e.printStackTrace();
}finally{
try{
if(rs != null){
rs.close();
}}catch(SQLException e){
e.printStackTrace();
}
try{
if(stmt != null){
stmt.close();
}}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn != null){
conn.close();
}}catch(SQLException e){
e.printStackTrace();
}
}
处理查询结果集,图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xuln97Tx-1648281348661)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220228100814055.png)]
SQL注入现象(黑客常用)
-
原因:用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。
String sql = "select * from t_users where user = '"+loginName+"' and password = '"+loginPwd+"'"; //以上正好完成了sql语句的拼接,一下的代码含义是,发送sql语句给DBMS,DBMS进行sql编译。 //正好将用户提供的“非法信息”编译进去,导致了原sql语句的含义被扭曲了 rs = stm.executeQuery(sql);
-
如何解决:
/*只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。 要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement PreparedStatement接口继承了java.sql.Statement,属于预编译的数据库操作对象。 其原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值。 只能用于条件,表名等不能使用,例如从emp表中查ename,sal>800的名字,只有800可以用?替代 */ PreparedStatement ps = null; //在3中获取预编译的数据库操作对象 //SQL语句的框子。其中一个?表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来。 String sql = "select * from t_users where user = ? and password = ?"; //程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。 ps = conn.prepareStatement(sql); //给占位符?传值(第一个?下标是1,以此类推,JDBC中所有下标从1开始) ps.setString(1,loginName); ps.setString(2,loginPwd); //4.执行sql语句 rs = ps.executeQuery();
对比Statement和PreparedStatement?
- Statement存在sql注入问题,PreparedStatement解决了sql注入问题
- Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些
- PreparedStatement会在编译阶段做类型的安全检查
- 什么情况下必须使用Statement?
- 业务方面要求必须支持SQL注入的时候,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement
- 例如:用户在控制台输入desc就是降序,asc就是升序的时候,就需要进行sql语句的拼接,然后就要用Statement
事务
/*JDBC事务机制:
1.JDBC中的事务是自动提交的,只要执行任意一条DML语句,则自动提交一次。
在java中使用代码
*/
conn.setAutoCommit(false);
//在try最后一行手动提交数据
conn.commit();
//在catch中,如果conn不=null,则要回滚
if(conn != null){
conn.rollback();
}
JDBC工具类的封装
模糊查询
String sql = "select ename from emp where ename like ?";
ps.conn.prepareStatement(sql);
ps.setString(1,"_A%");
ps.executeQuery();
行级锁
//悲观锁(行级锁,for update),在这个事务没有结束之前,emp表中的ename,job,sal三个数据不能进行改变
select ename,job,sal from emp where job = 'MANAGER' for update;
悲观锁:事务必须排队执行,数据锁住了,不允许并发。
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。
事务1--》读取到版本号1.1
事务2--》读取到版本号1.1
其中事务1先修改了,修改之后看了版本号是1.1,于是提交修改的数据,将版本号修改为1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2,和它最初读的版本号不一致。回滚。