JDBC-扩展
1.需求:模拟用户登录功能的实现
2.业务描述:程序运行的时候,提供一个输入接口,可以让用户输入用户名和密码,并提交信息,JAVA连接数据库核实信息,合法,登录成功,不合法,登陆失败。
3.数据准备:需要一个建模工具:PowerDesigner
package cc.bb.aa; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Ui_user { public static void main(String[] args) { //1.初始化界面: Map<String,String> userLogininfo=initUI();//需要返回一个给用户输入的对象,用来验证用户名和密码 //2.验证用户名和密码 boolean loginSuccess =Login(userLogininfo); //3.输出结果: System.out.println(loginSuccess?"登录成功":"登录失败"); } /** * 用户登录 * @param userLogininfo用户信息 * @return false表示失败,true表示成功 * */ private static boolean Login(Map<String, String> userLogininfo) { //JDBC代码 //打一个boolean的一个标记 boolean loginSuccess=false; Connection conn = null; Statement stmt = null; ResultSet rs=null; try { conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root",""); //statement专门执行SQL语句的 stmt=conn.createStatement();//create statement对象 //专门执行DML语句 //注意:这里补充一个知识点:双引号里面添加两个加号意思是变量所代表的值的字符串:格式为:"+loginInfo.get("loginName")+"; //返回值是“影响数据库中的记录条数” String sql="select * from user where name='"+userLogininfo.get("loginName")+"' and password='"+userLogininfo.get("loginPwd")+"'"; rs=stmt.executeQuery(sql); //处理结果集: if(rs.next()) { //登录成功 loginSuccess=true; }else { //登陆失败 loginSuccess =false; } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally {//这里关闭数据,释放资源 try{ if(stmt!=null){ stmt.close(); } }catch(SQLException e){ e.printStackTrace(); } try{ if(conn!=null){ conn.close(); } }catch(SQLException e){ e.printStackTrace(); } } return loginSuccess; } /** * 初始化界面 * @return 用户输入的用户名和密码等登录信息 * */ private static Map<String, String> initUI() { // TODO Auto-generated method stub Scanner scanner=new Scanner(System.in); System.out.println("用户名:"); String loginName=scanner.nextLine(); System.out.println("密码:"); String loginPwd=scanner.nextLine(); Map<String,String> userLoginInfoMap=new HashMap<>(); userLoginInfoMap.put("loginName", loginName); userLoginInfoMap.put("loginPwd", loginPwd); return userLoginInfoMap; } } fdsa fdsa' or '1'='1
二:SQL注入:
1.导致注入的根本原因:
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入
2.解决SQL注入:
/** *只要用户提供的信息中不参与SQL语句的编译过程,问题就解决了 *及时用户提供的信息中含有SQL语句的关键字,但是没有参加编译,不起作用 *想要用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement *PreparedStatement接口继承了java.sql.Statement *PreparedStatement是属于预编译的数据库操作对象 *PreparedStatement的原理是:与预先对SQL语句的框架进行编译,然后再给SQL语句传“值” * *解决SQL注入的关键是什么? * 用户提供的信息中含有sql语句的关键字,,但是这些关键字并没有参与编译,不起作用 */
注意:跟之前的使用statement相比较,还是有很大的区别的
//提前打标记: boolean loginSuccess=false; //单独定义变量 String loginName=userLogininfo.get("loginName"); String loginPwd=userLogininfo.get("loginPwd"); //提前定义: Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; //1.注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.获取连接 conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root",""); //3.获取与预编译的数据库操作对象 //SQL语句的框架,其中一个?,表示以一个占位符,一个?将来接受一个“值”,注意:占位符不能使用单引号括起来 String sql="select * from user where name=? and password=?"; //程序执行到此,会发送一个sql语句框子给DBMS,然后DBMS进行sql语句的预先编译 ps=conn.prepareStatement(sql); //给占位符?传值(第一个问好下标是1,第二个问好下标是2,JBDBC中所有的下标都是从1开始) ps.setString(1,loginName); ps.setString(2,loginPwd); //4.执行sql rs=ps.executeQuery(); //5.处理结果集 if(rs.next()){ //登录成功 loginSuccess=true; }else{ //登录失败 loginSuccess=false; } return loginSuccess;
注意:这里没有使用try,catch块,而且资源也没有finally释放,记得之后看代码的时候,对应前面的代码来看
三:使用preparedStatement
进行增删查改:
//1.注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.获取连接 conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root",""); //3.获取预编译的操作对象 String sql="insert into dept(deptno,dname,loc) values(?,?,?)";//这里写增删查改语句就好了,不会下去自己看看把 ps=conn.prepareStatement(sql); ps.setInt(1,60); ps.setString(2,"销售部"); ps.setString(3,"上海"); //4.执行SQL int count=ps.executeUpdate(); System.out.println(count);
四:JDBC事务提交:
1.JDBC的事务时自动提交:只要执行任意一条DML语句,则自动提交一次,但是实际业务当中,通常都是N条DML语句共同联合才能完成
五:乐观锁和悲观锁:
乐观锁:事务不需要排队,支持并发
悲观锁:书屋排队执行,数据锁住了,不允许并发
一、悲观锁
顾名思义,悲观锁就是考虑事情发生的最坏情况。默认认为一定会有其它线程抢着修改当前线程正在使用的数据。分为,读锁和写锁。
-
读锁,又称共享锁,可以被其它线程所共享,所有线程都可以添加该锁。当没有其它线程添加读锁时,本线程可以修改数据,否则将无法修改数据。
-
写锁,也成排它锁,不能被共享。只要有线程添加该锁时,直到事务提交之前,其它线程都无法添加该锁。
如何实现(数据库提供实现方法):
\1. 读锁
select * from table lock in share mode
\2. 写锁
select * from table for update
二、乐观锁
乐观锁,认为没有其它线程和本线程同时修改数据。一般使用版本号来控制(类似于SVN版本控制器),在提交事务前,比较自身持有的版本号和数据库中存在的版本号,只有自身持有的版本号大于数据库中的版本号时才能修改数据,否则无法修改数据。
例如:
在hibernate中,提供了乐观锁的解决方案
首先,在实体类中,添加一个字段代表版本号,并添加get/set方法。
然后在实体的配置文件中添加version属性
结果:在测试代码中加入断点,当程序运行完该断点后,立即修改数据库t_customer表中version属性,使其加1,放行代码,结果将出现SatleObjectStateException异常。