JDBC学习

    科技2022-07-13  130

    JDBC 的学习

    1:JDBC是什么?2:JDBC的本质是什么?3:模拟JDBC的本质4:JDBC 编程六步5:利用反射注册驱动6:结合配置文件读取数据库7:处理查询结果集(重点)8:模拟用户登录业务9:jdbc事务机制10:JDBC工具类的封装11:悲观锁与乐观锁

    1:JDBC是什么?

    Java DataBase Connectivity (java语言连接数据库)

    2:JDBC的本质是什么?

    是SUN公司制定的一套接口(interface) 接口都有调用者和实现者 面向接口去调用,面向接口实现类,都属于面向接口编写

    为什么要面向接口编程? 解耦合,降低程序的耦合度,提高程序的扩展力 多态机制,就是典型的面向抽象编程。(不要面向具体编程)

    //建议: Animal a = new Cat(); Animal a = new Dog(); // 喂养的方法 public void feed(Animal a){ // 面向父类型编程。 } //不建议: Dog d = new Dog(); Cat c = new Cat();

    为什么SUN制定一套JDBC接口呢? 因为每一个数据库的底层实现原理都不一样。 Oracle数据库有自己的原理。 MySQL数据库也有自己的原理。 MS SqlServer数据库也有自己的原理。 … 每一个数据库产品都有自己独特的实现原理。

    JDBC的本质到底是什么? 一套接口。

    3:模拟JDBC的本质

    1:JDBC接口的制定 2:数据库厂商实现JDBC接口 3:程序员面向接口写代码(结合反射机制,properties文件)

    import java.util.ResourceBundle; /** * java程序员 * 不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码 * 面向接口编程,面向抽象编程,不面向具体编程 * 面向接口去创建对象 * */ public class JavaMy { public static void main(String[] args) throws Exception { JDBC jdbc = new MySQL(); jdbc.getConnection(); JDBC jdbc1 = new Oracal(); jdbc1.getConnection(); //上面这两种方式都是通过面向接口编程 //也可以利用反射机制去创建对象 ResourceBundle db = ResourceBundle.getBundle("DB"); String classname = db.getString("classname"); Class c = Class.forName(classname); JDBC jdbc2 =(JDBC)c.newInstance(); jdbc2.getConnection(); // new JDBC() { // @Override // public void getConnection() { // // } // }匿名内部类 利用接口去接收对象 } }

    4:JDBC 编程六步

    第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)

    第二步:建立连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)

    第三步:获取数据库操作对象(专门执行sql语句的对象)

    第四步:执行SQL语句(DQL DML…)

    第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)

    第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)

    jdbc完成insert

    import java.sql.*; public class Test02 { public static void main(String[] args) { // 注册驱动 Statement statement = null; Connection connection = null; try { Driver driver = new com.mysql.jdbc.Driver(); // 多态,父类型的引用指向子类型的对象 DriverManager.registerDriver(driver); // 使用 父类型的数据类型去接收子类型对象 String url = "jdbc:mysql://127.0.0.1:3306/learn"; String user = "root"; String password = "333"; connection = DriverManager.getConnection(url, user, password); /** * Creates a <code>Statement</code> object for sending * SQL statements to the database. * 获取数据库操作对象 */ statement = connection.createStatement(); String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','宁波')"; int i = statement.executeUpdate(sql);//专门执行DML语句(insert delete update)返回数据库中受影响的记录条数 System.out.println(i == 1 ? "保存成功" : "保存失败"); } catch (SQLException e) { e.printStackTrace(); } finally { //保证资源一定释放,需要在finally语句中将资源关闭,并且遵循从小到大的顺序依次关闭 // 分别对其进行try catch if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }

    5:利用反射注册驱动

    利用反射机制让静态代码块执行,参数是字符串,可以写入配置文件 不需要接收返回值。

    6:结合配置文件读取数据库

    package com.fangjun.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ResourceBundle; /** * 将连接数据库的所有文件写入到配置文件中 */ public class Test02 { public static void main(String[] args) { //利用资源绑定器绑定资源 ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver1 = bundle.getString("driver"); String url1 = bundle.getString("url"); String user1 = bundle.getString("user"); String pass1 = bundle.getString("pass"); Statement statement=null; Connection root=null; try { Class.forName(driver1);//底层会执行对象的静态代码块 root = DriverManager.getConnection(url1,user1,pass1);//获取数据库连接,三个参数,url,user,passwd statement = root.createStatement();//数据库执行的对象 String sql="delete from dept where deptno=40"; int i = statement.executeUpdate(sql);//这个对象去执行sql语句 //DML语句:executeUpdate 专门执行增删改,返回int类型,受影响的条数 //执行查询的语句:executeQuery,这个Query语句会返回一个ResultSet System.out.println(i==1? "删除成功":"删除失败"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (root!=null){ try { root.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }

    7:处理查询结果集(重点)

    package com.fangjun.jdbc; import java.sql.*; import java.util.ResourceBundle; public class Test05 { public static void main(String[] args) { Connection connection=null; Statement statement =null; ResultSet resultSet=null;//结果集对象 ResourceBundle jdbc = ResourceBundle.getBundle("jdbc"); String driver = jdbc.getString("driver"); String url = jdbc.getString("url"); String user = jdbc.getString("user"); String pass = jdbc.getString("pass"); try { Class.forName(driver); connection=DriverManager.getConnection(url,user,pass); statement=connection.createStatement(); String sql="select empno,ename,sal from emp"; resultSet = statement.executeQuery(sql); while (resultSet.next()){ String empno = resultSet.getString(1); String ename = resultSet.getString(2); String sal = resultSet.getString(3); System.out.println(empno+" "+ename+" "+sal); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }

    8:模拟用户登录业务

    1)模拟登陆业务(存在SQL注入问题)

    package com.fangjun; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; public class Userlogin { public static void main(String[] args) { Map<String,String> userLoginInfo= initUI(); boolean flag = login(userLoginInfo); System.out.println(flag? "登录成功":"登录失败"); } private static boolean login(Map<String, String> userLoginInfo) { ResourceBundle jdbc = ResourceBundle.getBundle("jdbc"); String driver = jdbc.getString("driver"); String url = jdbc.getString("url"); String user = jdbc.getString("user"); String pass = jdbc.getString("pass"); Connection connection=null; Statement statement=null; ResultSet resultSet=null; boolean loginflag=false; String loginName = userLoginInfo.get("loginName"); String loginPwd = userLoginInfo.get("loginPwd"); try { Class.forName(driver); connection = DriverManager.getConnection(url, user, pass); statement = connection.createStatement(); String sql="select *from t_user where loginName='"+loginName+"' and loginPwd='"+loginPwd+"'"; System.out.println(sql); resultSet = statement.executeQuery(sql); //只有一条 if (resultSet.next()){ loginflag=true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return loginflag; } private static Map<String, String> initUI() { Scanner scanner = new Scanner(System.in); System.out.println("Please input the Username"); String Username = scanner.nextLine(); System.out.println("Please input the passWord"); String password = scanner.nextLine(); HashMap<String, String> Userloginfo = new HashMap<>(); Userloginfo.put("loginName",Username); Userloginfo.put("loginPwd",password); return Userloginfo; } }

    2)关于sql注入

    select *from t_user where loginName='fdsa' and loginPwd='fdsa' or '1'='1'

    查询结果如下 用户注入的信息含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,导致SQL原意被扭曲。

    3)如何解决SQL注入问题 使用PreparedStatement 预先对SQL语句进行逻辑的编译,后续输入含有逻辑的SQL语句被当做普通字符串,不再参与数据库查询逻辑的操作。

    package com.fangjun; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; public class Userlogin001 { public static void main(String[] args) { Map<String,String> userLoginInfo= initUI(); boolean flag = login(userLoginInfo); System.out.println(flag? "登录成功":"登录失败"); } private static boolean login(Map<String, String> userLoginInfo) { ResourceBundle jdbc = ResourceBundle.getBundle("jdbc"); String driver = jdbc.getString("driver"); String url = jdbc.getString("url"); String user = jdbc.getString("user"); String pass = jdbc.getString("pass"); Connection connection=null; PreparedStatement statement=null; //这里修改成 PreparedStatement ResultSet resultSet=null; boolean loginflag=false; String loginName = userLoginInfo.get("loginName"); String loginPwd = userLoginInfo.get("loginPwd"); try { Class.forName(driver); connection = DriverManager.getConnection(url, user, pass); //sql语句的模板,?表示一个占位符;注意:占位符不要使用单引号 String sql="select *from t_user where loginName = ? and loginPwd = ?"; //执行到此处,会发送SQL模板给DBMS,预先对SQL语句的逻辑进行编译,之后不在会添加其他逻辑,逻辑符号位普通字符 statement=connection.prepareStatement(sql); //此处给站位符号进行传值操作,第一个问号下标为1 statement.setString(1,loginName); statement.setString(2,loginPwd); resultSet = statement.executeQuery(); //依旧是需要执行,需要返回结果集 //只有一条 if (resultSet.next()){ loginflag=true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return loginflag; } private static Map<String, String> initUI() { Scanner scanner = new Scanner(System.in); System.out.println("Please input the Username"); String Username = scanner.nextLine(); System.out.println("Please input the passWord"); String password = scanner.nextLine(); HashMap<String, String> Userloginfo = new HashMap<>(); Userloginfo.put("loginName",Username); Userloginfo.put("loginPwd",password); return Userloginfo; } }

    4)对比Statement 与 PreparedStatement a:前者存在SQL注入问题,后者解决此问题 b:PreparedStatement 预先编译含有占位符的SQL语句,后续只需要简单传值,只编译一次,效率较高一些。 c:PreparedStatement 在编译期间会自动进行类型的安全检查。 注意:Statement 支持SQL语句的拼接,凡是业务需要进行SQL语句拼接的,必须使用Statement ,传值使用PreparedStatement

    9:jdbc事务机制

    jdbc事务自动提交,只要执行任意一条DML语句,则自动提交一次;这是JDBC默认的事务行为。 结论:JDBC中只要执行任意一条DML语句就提交一次。 代码验证:

    package com.fangjun; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ResourceBundle; public class JdbcTest001 { public static void main(String[] args) { ResourceBundle jdbc = ResourceBundle.getBundle("jdbc"); String driver = jdbc.getString("driver"); String url = jdbc.getString("url"); String user = jdbc.getString("user"); String pass = jdbc.getString("pass"); Connection connection=null; PreparedStatement statement=null; //这里修改成 PreparedStatement ResultSet resultSet=null; try { Class.forName(driver); connection = DriverManager.getConnection(url,user,pass); statement = connection.prepareStatement("update dept set dname=? where deptno=?"); statement.setString(1,"x"); statement.setString(2,"30"); int count = statement.executeUpdate(); System.out.println(count); //执行到这里数据库已经被修改 statement.setString(1,"y"); statement.setString(2,"20"); count = statement.executeUpdate(); System.out.println(count); } catch (Exception e) { e.printStackTrace(); } } } (补充 代码演示银行账户转账 总金额会产生错误)

    package com.fangjun;

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ResourceBundle;

    public class JdbcTest001 { public static void main(String[] args) { ResourceBundle jdbc = ResourceBundle.getBundle(“jdbc”); String driver = jdbc.getString(“driver”); String url = jdbc.getString(“url”); String user = jdbc.getString(“user”); String pass = jdbc.getString(“pass”); Connection connection=null; PreparedStatement statement=null; //这里修改成 PreparedStatement ResultSet resultSet=null; try { Class.forName(driver); connection = DriverManager.getConnection(url,user,pass); statement = connection.prepareStatement(“update t_act set balance=? where actno=?”); statement.setDouble(1,10000); statement.setInt(2,111); int count = statement.executeUpdate(); System.out.println(count); //执行到这里数据库已经被修改

    String s=null; s.toString(); statement.setDouble(1,10000); statement.setInt(2,222); count += statement.executeUpdate(); System.out.println(count==2? "right":"error"); } catch (Exception e) { e.printStackTrace(); } }

    }

    将JDBC的事务修改成手动提交

    package com.fangjun; import java.sql.*; import java.util.ResourceBundle; public class JdbcTest001 { public static void main(String[] args) { ResourceBundle jdbc = ResourceBundle.getBundle("jdbc"); String driver = jdbc.getString("driver"); String url = jdbc.getString("url"); String user = jdbc.getString("user"); String pass = jdbc.getString("pass"); Connection connection=null; PreparedStatement statement=null; //这里修改成 PreparedStatement ResultSet resultSet=null; try { Class.forName(driver); connection = DriverManager.getConnection(url,user,pass); connection.setAutoCommit(false); //将jdbc的自动提交机制进行关闭 statement = connection.prepareStatement("update t_act set balance=? where actno=?"); statement.setDouble(1,10000); statement.setInt(2,111); int count = statement.executeUpdate(); System.out.println(count); //执行到这里数据库已经被修改 String s=null; s.toString(); statement.setDouble(1,10000); statement.setInt(2,222); count += statement.executeUpdate(); System.out.println(count==2? "right":"error"); //程序执行到这里如果没有发生意外,可以提交所有事务.手动提交数据 connection.commit(); } catch (Exception e) { //如果出现意外,在这里回滚事务,确保数据的安全 if (connection!=null){ try { connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } e.printStackTrace(); } } }

    10:JDBC工具类的封装

    package com.fangjun; import java.sql.*; public class DbTools { //工具类构造方法建议私有化 private DbTools() { } //将注册驱动的行为执行一次,在类加载的时候执行 static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取数据库连接对象,异常抛出,在主程序中进行捕捉 * @return * @throws SQLException */ public static Connection getCollection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/learn","root","333"); } /** * 关闭资源 * @param resultSet * @param statement 面向接口编程,Statement是父接口 * @param connection */ public static void close(ResultSet resultSet, Statement statement,Connection connection){ if (resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

    JDBC借助工具类实现模糊查询

    import com.fangjun.DbTools; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class MohuQuery { public static void main(String[] args) { Connection connection=null; PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { connection = DbTools.getCollection(); preparedStatement = connection.prepareStatement("select ename from emp where ename like ?"); preparedStatement.setString(1,"_A%");//查询第二个字母为A resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ String ename = resultSet.getString("ename"); System.out.println(ename); } } catch (Exception e) { e.printStackTrace(); } finally { DbTools.close(resultSet,preparedStatement,connection); } } }

    11:悲观锁与乐观锁

    for update 悲观锁 锁住一行记录(行级锁) jdbc代码演示行级锁 加行级锁

    package com.fangjun; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 代码演示行级锁,这个进行查询,使用行级锁 */ public class hangjisuo { public static void main(String[] args) { Connection connection=null; PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { connection = DbTools.getCollection(); connection.setAutoCommit(false);//关闭jdbc的自动提交机制 preparedStatement = connection.prepareStatement("select ename,job,sal from emp where job=? for update"); preparedStatement.setString(1,"MANAGER"); resultSet=preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getString("ename")+","+resultSet.getString("job")+","+resultSet.getDouble("sal")); } connection.commit();//开启jdbc的手动提交机制 } catch (Exception e) { if (connection!=null){ //avoid nullpointexception try { connection.rollback();//遇到错误的自动回滚事务机制 } catch (SQLException e1) { e1.printStackTrace(); } } e.printStackTrace(); }finally { DbTools.close(resultSet,preparedStatement,connection); } } }

    另外的代码对锁定数据执行修改操作

    package com.fangjun; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 操作被锁定的数据 */ public class hangjisuo01 { public static void main(String[] args) { Connection connection=null; PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { connection = DbTools.getCollection(); connection.setAutoCommit(false);//关闭自动提交机制 preparedStatement=connection.prepareStatement("update emp set sal=sal*1.1 where job=?"); preparedStatement.setString(1,"MANAGER"); int count = preparedStatement.executeUpdate(); System.out.println(count); connection.commit();//手动提交机制 } catch (Exception e) { if (connection!=null){ try { connection.close(); } catch (SQLException e1) { e1.printStackTrace(); } } e.printStackTrace(); }finally { DbTools.close(null,preparedStatement,connection); } } }
    Processed: 0.009, SQL: 8