03_JavaWeb||day05_JDBC基础【重点掌握】||day05_1_JDBC基础【重点掌握第五点和会使用JDBCUtils工具类,了解JDBC事务处理位置即可】

今日内容【重点掌握】

  1. JDBC基本概念
  2. 快速入门
  3. 对JDBC中各个接口和类详解

1. JDBC:

  1. 概念:Java数据库连接(Java DataBase Connectivity)
    • Java语言操作数据库
  2. JDBC本质
    • 其实是官方(sun公司)定义的一套操作++所有关系型数据库的规则++,即接口
    • 各个数据库厂商去实现这套接口,提供数据库驱动jar包。
    • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
  3. 快速入门
    • 步骤:
      1. 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
        1. 复制到项目的libs目录下
        2. 右键libs文件–>Add As Library(这样了才真正的加入到了项目中)
      2. 注册驱动(编写代码)
            Class.forName("com.mysql.jdbc.Driver");
        
        • Class.forName("");的作用是将代码加载进内存
        • 会有异常:正常写的时候要处理
      3. 获取数据库的连接对象(Connection)
            //数据库db3的url,账号,密码
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password");
        
      4. 定义sql
            String sql = "update account set balance = 500 where id = 1";
        
      5. 获取执行sql语句的对象(Statement)
            Statement stmt = conn.createStatement();
        
      6. 执行sql,接收返回结果
            int count = stmt.executeUpdate(sql);
        
      7. 处理结果
            System.out.println(count);
        
      8. 释放资源
            stmt.close();
            conn.close();
        
  4. ++详解各个对象++

    1. ++DriverManager:驱动管理对象++

    1. 是一个类
    2. 功能:
      1. 注册驱动告诉程序该使用哪一个数据库驱动jar包
        • 方法:static void registerDriver(Driver driver):注册与给定的驱动程序(DriverManager)
        1. 写代码时使用的是Class.forName(“com.mysql.jdbc.Driver”);【重点掌握】//作用是将代码(类文件)加载进内存
          • ++这样有一些代码会随着类的加载而自动执行(静态代码块static)++
          • 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
                static{
                    try{
                        java.sql.DriverManager.registerDriver(new Driver());
                    }catch(SQLException E){
                        throw new RuntimeException("Can't register driver!");
                    }
                }
            
        2. 所以底层实现就是DriverManager,只不过使用Class.forName(“com.mysql.jdbc.Driver”);简单【总结】
        • 注:mysql之后的驱动jar包可以省略注册驱动的步骤【一般写上】
          • 在jar包–>META-INF–>services–>java.sql.Driver中
        • 注:Class.forName会有异常:正常写的时候要处理
      2. 获取数据库连接
        • 方法:static Connection getConnection(String url, String user, String password);//静态方法,可以直接通过类名调用
        • 这个也是会抛出异常,和Class.forName抛出的异常放到一起
        • 参数:
          • url:指定连接的路径
            • 语法【重点】(每个sql语言都不太一样):
              • MySQL
                  //ip找到某计算机,端口号可以找到该计算机上的MySQL
                  jdbc:mysql://ip地址(域名):端口号/数据库名称
              
              • 例子:
                  jdbc:mysql://localhost:3306/db3
              
              • 细节:如果访问本地则ip和端口号可以省略(了解)
                  jdbc:mysql:///db3
              
          • user:用户名
          • password:密码

    2. ++Connection:数据库连接对象++

    • 是个接口
    1. 功能:
      1. 获取执行sql的对象
        • 方法:
        • Statement createStatement():【重点掌握】
          • 例子:获取==执行sql语句的对象(Statement是返回值类型)
              Statement stmt = conn.createStatement();
          
        • PreparedStatement preparedStatement(String sql)
      2. 管理事务
        1. 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(自动变为手动),即为开启事务
        2. 提交事务:commit()
        3. 回滚事务:rollback()

    3. ++Statement:执行sql的对象【重点】++

    • 是个接口
    1. 功能:
      1. 执行sql
        • 方法:
        • boolean execute(String sql):可以执行任意的sql–>(了解)
        • int executeUpdate(String sql):【重点掌握】–>执行DML(增删改insert,delete,update)语句、DDL(表和库的操作create,alter,drop)语句
          • 注:返回值就是执行语句影响的行数
            • 执行DDL的时候不返回(默认0)
            • 可以通过这个影响的行数判断DML语句是否执行成功,返回值>0则执行成功,反之,则失效
        • ResultSet executeQuery(String sql):【重点掌握】–>执行DQL(查询select)语句
          • 返回值是:结果集对象(就是查询sql后显示的呢个结果)
    2. 练习:
      1. account表 添加一条记录(insert)
        • 注:释放资源放到finally里面(不管代码对不对都要释放)
        • 导入sql包不要看错了
            import java.sql.Statement;
            import java.sql.Connection;
            import java.sql.DriverManager;
            import java.sql.SQLException;
            
            public class Test {
            	public static void main(String[] args) {
            		Statement stmt = null;
            		Connection conn = null;
            		try {
            			//1. 注册驱动
            			Class.forName("com.mysql.jdbc.Driver");
            			//2. 定义sql
            			String sql = "INSERT INTO account value(null, '王五', 3000)";
            			//3. 获取Connection对象
            			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password");
            			//4. 获取执行sql的对象:Statement
            			stmt = conn.createStatement();
            			//5. 执行sql
            			int count = stmt.executeUpdate(sql);	//影响的行数
            			//处理的结果
            			System.out.println(count);
            			if(count > 0) {
            				System.out.println("添加成功");
            			}else {
            				System.out.println("添加失败");
            			}
            		} catch (ClassNotFoundException | SQLException e) {
            			// TODO Auto-generated catch block
            			e.printStackTrace();
            		}finally{
            			//为了避免空指针异常
            			//7. 释放资源
            			if(stmt != null) {
            				try {
            					stmt.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            			if(conn != null) {
            				try {
            					conn.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            		}
            	}
            }
        
      2. account表 修改记录
        • 修改id为3的钱为1500
            import java.sql.Statement;
            import java.sql.Connection;
            import java.sql.DriverManager;
            import java.sql.SQLException;
            
            public class Test2 {
            	public static void main(String[] args) {
            		Connection conn = null;
            		Statement stmt = null;
            		try {
            			//1. 驱动注册
            			Class.forName("com.mysql.jdbc.Driver");//**
            			//2. 编写sql
            			String sql = "UPDATE account SET balance=1500 WHERE id=3";
            			//3.创建Connection对象
            			conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password");
            			//4. 获取执行sql对象
            			stmt = conn.createStatement();
            			//5.执行sql
            			int count = stmt.executeUpdate(sql);
            			//6.执行结果
            			System.out.println(count);
            			if(count > 0) {
            				System.out.println("执行成功");
            			}else {
            				System.out.println("执行失败");
            			}
            		} catch (ClassNotFoundException | SQLException e) {
            			// TODO Auto-generated catch block
            			e.printStackTrace();
            		}finally {
            			//7.释放资源
            			if(stmt != null) {
            				try {
            					stmt.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            			if(conn != null) {
            				try {
            					conn.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            		}
            	}
            }
        
      3. account表 删除一条记录
        • 删除id=3的记录
            import java.sql.Connection;
            import java.sql.DriverManager;
            import java.sql.SQLException;
            import java.sql.Statement;
            
            public class Test3 {
            	public static void main(String[] args) {
            		Connection conn = null;
            		Statement stmt = null;
            		try {
            			//1.驱动注册
            			Class.forName("com.mysql.jdbc.Driver");
            			//2.sql语句
            			String sql = "DELETE FROM account WHERE id = 3";
            			//3.获取Connection对象
            			conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password");	//***
            			//4.获取执行sql对象
            			stmt = conn.createStatement();
            			//5.执行sql语句
            			int count = stmt.executeUpdate(sql);
            			//6.执行结果
            			System.out.println(count);
            			if(count > 0) {
            				System.out.println("删除成功");
            			}else {
            				System.out.println("删除失败");
            			}
            		} catch (ClassNotFoundException | SQLException e) {
            			// TODO Auto-generated catch block
            			e.printStackTrace();
            		}finally {
            			if(stmt != null) {
            				try {
            					stmt.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            			if(conn != null) {
            				try {
            					conn.close();
            				} catch (SQLException e) {
            					// TODO Auto-generated catch block
            					e.printStackTrace();
            				}
            			}
            		}
            		
            	}
            }
        

    4. ++ResultSet:结果集对象(用来封装查询结果)++

    • 是一个接口
    1. 意义:从ResultSet结果集对象中,把查出来的结果数据拿出来
      • 默认在第一行,所以先向下
      • 执行方法让游标一行一行向下进行数据获取
        • 每次获取的是该行的某一列数据
    2. 方法:
      1. boolean next():++游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果没有数据,返回false++
      2. getXxx(参数):获取数据
        • Xxx:代表数据类型
          • 比如获取id的时候getInt()
          • 获取name的时候getString()
        • 参数:(体现了方法的重载)
          1. Int:代表列的编号(从1开始)
            • 获取id的列数据
              • getInt(1):表示获取id列数据
          2. String:代表列的名称。
            • 获取balance列的数据(余额)
              • getDouble(“balance”);
    3. 演示:(将来不是这样使用的,这里只是基本使用)
      • 结果集对象也是资源,所以也需要释放
          import java.sql.Connection;
          import java.sql.DriverManager;
          import java.sql.ResultSet;
          import java.sql.SQLException;
          import java.sql.Statement;
          
          public class Test4 {
          	public static void main(String[] args) {
          		Connection conn = null;
          		Statement stmt = null;
          		ResultSet rs = null;
          		try {
          			//1.注册驱动
          			Class.forName("com.mysql.jdbc.Driver");
          			//2.sql语句
          			String sql = "SELECT * FROM account";
          			//3.获取Connection对象
          			conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password");
          			//4.创建获取执行sql对象
          			stmt = conn.createStatement();
          			//5.执行sql(DQL-->用executeQuery)
          			rs = stmt.executeQuery(sql);
          			//6. 执行结果(获取的数据)
          			//6.1先让游标向下移动一行
          			rs.next();
          			//6.1获取数据
          			int id = rs.getInt(1);
          			String name = rs.getString("name");
          			double balance = rs.getDouble("balance");
          			
          			//打印
          			System.out.println(id + "---" + name + "---" + balance);
          		} catch (ClassNotFoundException | SQLException e) {
          			// TODO Auto-generated catch block
          			e.printStackTrace();
          		}finally {
          			if(rs != null) {
          				try {
          					rs.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          			if(stmt != null) {
          				try {
          					stmt.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          			if(conn != null) {
          				try {
          					conn.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          		}
          		
          	}
          }
      
      • 出现的问题
        • 获取第二行的时候又要复制所有获取数据代码
    4. ResultSet真正使用方式
      1. 游标向下移动一行
      2. 判断是否有数据
      3. 获取数据
          import java.sql.Connection;
          import java.sql.DriverManager;
          import java.sql.ResultSet;
          import java.sql.SQLException;
          import java.sql.Statement;
          
          public class Test5 {
          	public static void main(String[] args) {
          		ResultSet rs = null;
          		Statement stmt = null;
          		Connection conn = null;
          		
          		try {
          			//1.注册驱动
          			Class.forName("com.mysql.jdbc.Driver");
          			//2.sql语句
          			String sql = "SELECT * FROM account";
          			//3.获取Connection对象
          			conn = DriverManager.getConnection("jdbc.mysql://localhost.3306/db3", "root", "password");
          			//4.获取执行sql对象
          			stmt = conn.createStatement();
          			//5.执行sql(DQL-->Query)
          			rs = stmt.executeQuery(sql);
          			
          			//6. 执行结果(获取的数据)
          			//循环判断游标是否是最后一行末尾
          			while(rs.next()) {
          				//6.1获取数据
          				int id = rs.getInt(1);
          				String name = rs.getString("name");
          				double balance = rs.getDouble("balance");
          				//打印
          				System.out.println(id + "---" + name + "---" + balance);
          			}
          		} catch (ClassNotFoundException | SQLException e) {
          			// TODO Auto-generated catch block
          			e.printStackTrace();
          		}finally {
          			if(rs != null) {
          				try {
          					rs.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          			if(stmt != null) {
          				try {
          					stmt.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          			if(conn != null) {
          				try {
          					conn.close();
          				} catch (SQLException e) {
          					// TODO Auto-generated catch block
          					e.printStackTrace();
          				}
          			}
          		}
          	}
          }
      
      • 练习:
        • 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
          1. 定义Emp类
          2. 定义方法 public List findAll(){}
          3. 实现方法 select * from emp;
            //Emp类(JavaBean)
            public class Emp {
                private int id;
                private String balance;
            
                public int getId() {
                    return id;
                }
            
                public void setId(int id) {
                    this.id = id;
                }
            
                public String getBalance() {
                    return balance;
                }
            
                public void setBalance(String balance) {
                    this.balance = balance;
                }
            
                @Override
                public String toString() {
                    return "Emp{" +
                            "id=" + id +
                            ", balance='" + balance + '\'' +
                            '}';
                }
            }
            
            //JdbcDemo类
            import java.sql.*;
            import java.util.LinkedList;
            import java.util.List;
            
            public class JdbcDemo {
                public static void main(String[] args){
                    List<Emp> list = new JdbcDemo().findAll();
                    System.out.println(list);
                }
            
            
                public List<Emp> findAll(){
                    Connection conn = null;
                    Statement stmt = null;
                    ResultSet rs = null;
                    List<Emp> listA = null;
            
                    try {
                        //1注册驱动
                        Class.forName("com.mysql.jdbc.Driver");
                        //2sql语句
                        String sql = "SELECT * FROM emp";
                        //3获取Connection对象
                        conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "psaaword");
                        //4.获取执行sql的对象
                        stmt = conn.createStatement();
                        //5.执行sql(查询的DQL,所以query)
                        rs = stmt.executeQuery(sql);
            
            
                        Emp emp = null;
                        //因为要返回所以写在外面
                        listA = new LinkedList<>();   //用来存储信息的集合
                        //获取信息后存到集合中
                        while (rs.next()){  //判断下一行是否有值
                            //先获取到数据库里面的值
                            int id = rs.getInt("id");
                            String balance = rs.getString("balance");
            
                            //创建emp对象并赋值
                            emp = new Emp();
                            //将值添加到emp类中
                            emp.setId(id);
                            emp.setBalance(balance);
            
                            //加到集合中
                            listA.add(emp);
                        }
            
                    } catch (ClassNotFoundException | SQLException e) {
                        e.printStackTrace();
                    }finally {
                        if(rs != null){
                            try {
                                rs.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                        if(stmt != null){
                            try {
                                stmt.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                        if(conn != null){
                            try {
                                conn.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            
                    return listA;
                }
            }
        

    5. ++PreparedStatement:表示预编译sql语句的对象【开发真正用的】(功能更多)++

    • Statement接口的子接口
    • 注:浏览顺序,先看JDBC工具类在回头来看该内容
    1. JDBC工具类中综合案例出现的问题:SQL注入问题【重点】
      1. SQL注入概念【*】在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。(会造成安全问题)
        • 例子:
        1. 输入用户随便(gxw),输入密码(特殊密码):
              //输入的特殊密码
              a' or 'a' = 'a
          
        2. 就会生成一段sql语句
              select * form user where username='gxw' and password='a' or 'a' = 'a'
          
          • or之前确实是false但是后面加的为true,or是或运算,所有就恒等于true了(就很危险)
      2. ++解决SQL注入问题++:使用PreparedStatement对象来解决
      3. 预编译的sql【重点】参数使用?作为占位符
      4. 步骤【真正要掌握的】:
        1. 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
        2. 注册驱动(编写代码)
          • Class.forName("");的作用是将代码加载进内存
        3. 获取数据库的连接对象(Connection)
        4. 定义sql
          • 注意:sql的参数使用?作为占位符【#】
          • 如:
                select * form user where username=? and password=?;
            
        5. 获取执行sql语句的对象(PreparedStatement
          • 调用:Connection.prepareStatement(String sql)方法
            • 要传递一个sql进来(不同之处)【注】
        6. 给?去赋值
          • 方法:setXxx(参数1,参数2);
            • 参数1:?的位置编号从1开始
            • 参数2:?的值
        7. 执行sql,接收返回结果(这里就不需要传递sql语句了
        8. 处理结果
        9. 释放资源
      5. 优化JDBC工具类中的登录方法:
            public class JdbcTest1 {
                public static void main(String[] args) {
                    //输入账号和密码
                    Scanner sc = new Scanner(System.in);
                    System.out.println("请输入账号");
                    String username = sc.nextLine();
                    System.out.println("请输入密码");
                    String password = sc.nextLine();
                    boolean login = new JdbcTest1().login(username, password);//非静态方法
                    if(login){
                        System.out.println("登录成功");
                    }else {
                        System.out.println("账号或者密码错误");
                    }
                }
            
            
                /*
                 * 登录方法,使用PreparedStatement实现
                 */
                public boolean login(String username, String password){
                    if(username == null || password == null){
                        return false;
                    }
                    Connection conn = null;
                    PreparedStatement pstmt = null;
                    ResultSet rs = null;
            
                    try {
                        //1.获取资源
                        conn = JDBCUtils.getConnection();
                        //2.sql语句
                        String sql = "SELECT * FROM username=? AND password=?";
                        //3.获取执行sql的对象
                        pstmt = conn.prepareStatement(sql);
                        //4.给?号赋值
                        pstmt.setString(1, username);
                        pstmt.setString(2, password);
                        //5.执行sql,不需要传递参数
                        rs = pstmt.executeQuery();
                        //判断
                        return rs.next();   //有下一行则返回true
            
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }finally {
                        JDBCUtils.close(rs, pstmt, conn);
                    }
            
                    return false;
                }
            }
        
      6. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作【重点】
        1. 防止SQL注入
        2. 效率更高

2. 抽取JDBC工具类 : JDBCUtils【工具类重点,会使用即可】

  1. 目的:简化书写
  2. 分析:【抽取的:注册驱动,获取连接对象,释放资源】
    1. 注册驱动也抽取
    2. 抽取一个方法获取连接对象【DriverManager.getConnection】
      • 需求:++不想传递参数(麻烦),还得保证工具类的通用性。++
      • 解决:配置文件
        jdbc.properties
        url=
        user=
        password=
    3. 抽取一个方法释放资源
  3. 代码实现【*】:
    • 配置文件:jdbc.properties
        //数据库db3的位置
        url=jdbc:mysql:///db3
        user=root
        password=root
        driver=com.mysql.jdbc.Driver
    
    • 方便调取,所以都是静态
        public class JDBCUtils {
            //先声明4个静态成员变量
            private static String url;
            private static String user;
            private static String password;
            private static String driver;
            
            /**
             * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
             */
            static{
                //读取资源文件,获取值。
        
                try {
                    //1. 创建Properties集合类。
                    Properties pro = new Properties();
        
                    //获取src路径下的文件的方式--->ClassLoader 类加载器
                    ClassLoader classLoader = JDBCUtils.class.getClassLoader();
                    URL res  = classLoader.getResource("jdbc.properties");
                    String path = res.getPath(); //System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
                    
                    
                    //2. 加载文件
                    pro.load(new FileReader(path));//读取字符文件
        
                    //3. 获取数据,赋值
                    url = pro.getProperty("url");
                    user = pro.getProperty("user");
                    password = pro.getProperty("password");
                    driver = pro.getProperty("driver");
                    //4. 注册驱动
                    Class.forName(driver);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        
        
            /**
             * 获取连接
             * @return 连接对象
             */
            public static Connection getConnection() throws SQLException {
        
                return DriverManager.getConnection(url, user, password);
            }
        
            /**
             * 释放资源【dml时候,增删改时】
             * @param stmt
             * @param conn
             */
            public static void close(Statement stmt,Connection conn){
                if( stmt != null){
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if( conn != null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        
        
            /**
             * 释放资源【dql时候,查询时】
             * @param rs
             * @param stmt
             * @param conn
             */
            public static void close(ResultSet rs,Statement stmt, Connection conn){
                if( rs != null){
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if( stmt != null){
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if( conn != null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        
        }
    
    • 什么时候使用静态代码块
      • 如果需要程序在启动之前就进行一些操作,以及一些经常用到却不占用多少内存的代码或变量直接写在static静态代码块中,当程序启动的时候随之初始化,这样可以节省部分后续操作的时间
      • 格式:
            static{
                
            }
        
    • ClassLoader 类加载器【先了解】
      1. 作用
        • 获取src路径下的(资源路径)文件的方式
        • 可以来加载我们的字节码文件进内存
      2. (JDBC工具类获取配置文件路径的)使用步骤【重点】
        1. 获取到ClassLoader,首先获取到对应的字节码文件对象
          • 类名.class对象
            • 其中一个getClassLoader()方法:获取到ClassLoader
              ClassLoader classloader = JDBCUtils.class.getClassLoader();
          
        2. ClassLoader一个方法:getResource(String name):传入一个文件名(配置文件),就可以获取Resource资源。
          • ++返回的是一个URL对象(表示:统一资源标识符/定位符):可以用来定位一个文件的绝对路径++
              URL res = classloader.getResource("jdbc.properties");
          
        3. 使用URL对象的一个方法,getPath():可以获取一个字符串路径
              String path = res.getPath();
          
        4. 最后就可以将获取到的path,写到加载文件中了(上面三步就可以获取对应的资源文件的绝对路径了)
    • Properties集合类
    • 双列集合,键值都是String,唯一一个可以直接结合IO流使用的集合类,++从流中读取文件,直接写入文件到流中++
    1. 读取资源文件获取值的步骤
      1. 创建Properties集合类
            Properties pro = new Properties();
        
      2. 加载文件:
        • Properties集合类中load()方法从字节输入流中读取键值对。参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。该方法常用于读取配置文件
            pro.load(new FileReader(path));
        
      3. 获取值
        • Properties集合类中getProperty()方法将搜索此属性列表中指定键的属性值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
        
  4. 演示JDBC工具类
        import java.sql.*;
        import java.util.LinkedList;
        import java.util.List;
        
        public class JdbcDemo {
            public static void main(String[] args){
                List<Emp> list = new JdbcDemo().findAll();
                System.out.println(list);
            }
        
        
            public List<Emp> findAll(){
                Connection conn = null;
                Statement stmt = null;
                ResultSet rs = null;
                List<Emp> listA = null;
        
                try {
                    //注册驱动和获取Connection对象直接换成:
                    conn = JDBCUtils.getConnection();
                    
                    
                    
                    //3sql语句
                    String sql = "SELECT * FROM emp";
                    //4.获取执行sql的对象
                    stmt = conn.createStatement();
                    //5.执行sql(查询的DQL,所以query)
                    rs = stmt.executeQuery(sql);
        
        
                    Emp emp = null;
                    //因为要返回所以写在外面
                    listA = new LinkedList<>();   //用来存储信息的集合
                    //获取信息后存到集合中
                    while (rs.next()){  //判断下一行是否有值
                        //先获取到数据库里面的值
                        int id = rs.getInt("id");
                        String balance = rs.getString("balance");
        
                        //创建emp对象并赋值
                        emp = new Emp();
                        //将值添加到emp类中
                        emp.setId(id);
                        emp.setBalance(balance);
        
                        //加到集合中
                        listA.add(emp);
                    }
        
                } catch (SQLException e) {  //ClassNotFoundException异常就不需要了
                    e.printStackTrace();
                }finally {
                    //直接使用JDBCUtils工具类
                    JDBCUtils.close(rs, stmt, conn);
                }
        
                return listA;
            }
        }
    
  • 练习:
    • 需求(给db4数据库):
      1. 通过键盘录入用户名和密码
      2. 判断用户是否登录成功
            * select * from user where username = "" and password = "";
        
        • 如果这个sql有查询结果,则成功,反之,则失败
    • 步骤:
      1. 创建数据库表 user
            CREATE TABLE USER(
        		id INT PRIMARY KEY AUTO_INCREMENT,
        		username VARCHAR(32),
        		PASSWORD VARCHAR(32)
        	
        	);
        
        	INSERT INTO USER VALUES(NULL,'zhangsan','123');
        	INSERT INTO USER VALUES(NULL,'lisi','234');
        
      2. 代码实现:
        • 配置文件中的路径要进行修改:
              //数据库现在在db4的位置
              url=jdbc:mysql:///db4
              user=root
              password=root
              driver=com.mysql.jdbc.Driver
          
        • 前提要有前面写好的JDBCUtils工具类和配置文件
            //创建一个类:JdbcTest1
            public class JdbcTest1 {
                public static void main(String[] args) {
                    //输入账号和密码
                    Scanner sc = new Scanner(System.in);
                    System.out.println("请输入账号");
                    String username = sc.nextLine();
                    System.out.println("请输入密码");
                    String password = sc.nextLine();
                    boolean login = new JdbcTest1().login(username, password);//非静态方法
                    if(login){
                        System.out.println("登录成功");
                    }else {
                        System.out.println("账号或者密码错误");
                    }
                }
            
            
                /*
                登录方法
                 */
                public boolean login(String username, String password){
                    if(username == null || password == null){
                        return false;
                    }
                    Connection conn = null;
                    Statement stmt = null;
                    ResultSet rs = null;
            
                    try {
                        //1.获取资源
                        conn = JDBCUtils.getConnection();
                        //2.sql语句
                        String sql = "SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'";
                        //3.获取执行sql的对象
                        stmt = conn.createStatement();
                        //4.执行sql
                        rs = stmt.executeQuery(sql);
                        //判断
                        return rs.next();   //有下一行则返回true
            
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }finally {
                        JDBCUtils.close(rs, stmt, conn);
                    }
            
                    return false;
                }
            }
        
        • 注:定义sql语句时,里面传入的username和password都是字符串,所以拼接的时候别忘了单引号
        • 存在的问题SQL注入问题,请看上面第五个内容有讲解【注】

3. 使用JDBC控制事务:

  1. 事务:一个包含多个步骤的业务操作。如果这个业务操作++被事务管理,则这多个步骤要么同时成功,要么同时失败++
  2. 操作JDBC事务处理的位置【重点】
    1. 开启事务:++执行sql之前++
      • 在获取到Connection对象之后开启–>(因为事务管理功能就属于Connection对象)
    2. 提交事务:++sql都执行完++
      • 在执行完sql语句之后
    3. 回滚事务:++catch中++
      • 在catch当中,且注意判断conn是否为空
  3. Connection对象的两个功能:
    1. 获取执行sql的对象
    2. 管理事务
  4. 所以使用Connection对象来管理事务
    1. 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(false–>手动:开启事务后如果没有提交事务,就会回滚事务),(true–>自动:每次执行完自动提交事务)
    2. 提交事务:commit()
    3. 回滚事务:rollback()
      • 注:只要有异常就让回滚(放到catch中)
      • 回滚的时候,conn有可能为null,所以在回滚前判断是否为空
            if(conn != null){
                conn.rollback();
            }
        
  5. 案例:银行转账
    • 注:catch一般写的范围比较大
        /**
         * 事务的操作
         */
        public class JDBCDemo3 {
            public static void main(String[] args) {
                Connection conn = null;
                PreparedStatement pstmt1 = null;
                PreparedStatement pstmt2 = null;
        
                try {
                    //1.获取数据库的链接
                    conn = JDBCUtils.getConnection();
        
                    ///开启事务
                    conn.setAutoCommit(false);
        
                    //2.定义sql
                    //2.1张三-500
                    String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";
                    //2.2李四+500
                    String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";
        
                    //3.获取执行sql对象
                    pstmt1 = conn.prepareStatement(sql1);
                    pstmt2 = conn.prepareStatement(sql2);
        
                    //4.给?赋值
                    pstmt1.setDouble(1, 500);
                    pstmt1.setInt(2, 1);
        
                    pstmt2.setDouble(1, 500);
                    pstmt2.setInt(2, 2);
        
                    //5.执行sql语句(DML所以,使用executeUpdate)
                    pstmt1.executeUpdate();
        
                    //手动制造一点问题,但能否回滚(可以)
                    int i = 3 / 0;
                    pstmt2.executeUpdate();
        
                    ///提交事务
                    conn.commit();
                } catch (Exception e) { //一般抓一个大的异常,防止多次添加
                    ///事务回滚
                    try {
                        if(conn != null){
                            conn.rollback();
                        }
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                    e.printStackTrace();
                }finally {
                    //这里写的不规范,只是为了演示事务
                    JDBCUtils.close(pstmt1, conn);
                    JDBCUtils.close(pstmt2, null);
                }
            }
        }
    
发布了42 篇原创文章 · 获赞 6 · 访问量 1114

猜你喜欢

转载自blog.csdn.net/qq_40572023/article/details/105524514