16. JDBC

1. JDBC初步

JDBC的概念

  • Java DataBase Connectivity , 用Java语言操作数据库

JDBC的本质

  • JDBC本质:sun公司定义的一套操作所有关系型数据库的规则接口。各个数据库厂商负责实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
    在这里插入图片描述

③ 入门准备

  • 预备:导入驱动 jar 包
  • 复制 mysql-connector-java-5.1.37-bin.jar 到 libs
  • 右键 Add As Library
  • jar 包里面都是 .class文件

④ 代码示例

        //1.注册驱动,把该类通过 Class.forName 加载进内存
        Class.forName("com.mysql.jdbc.Driver");
       
        //2.获取数据库连接对象 Connection ,本地java代码和数据库之间的桥梁
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
        
        //3.定义sql语句
        String sql = "update account set balance = 500 where id = 1";
        
        //4.获取执行sql的对象 Statement,因为Connection不能直接执行
        Statement stmt = conn.createStatement();
        
        //5.执行sql并接收返回的结果
        int count = stmt.executeUpdate(sql);
        
        //6.处理结果
        System.out.println(count);
        
        //7.释放资源,就像流一样要关闭
        stmt.close();
        conn.close();

2. JDBC对象详解

① DriverManager 驱动管理对象

功能 :注册驱动 + 获取数据库连接对象

1.注册驱动,告诉程序该使用哪一个数据库驱动 jar

	public class DriverManager {
		public static void registerDriver(Driver driver) 
		public static Connection getConnection(String url, String user, String password)
		...
	}									
  • 注意:mysql5后可以省略注册驱动的步骤,配置文件预先写好,更加面向接口编程
    在这里插入图片描述

2.获取数据库连接对象

	url:指定连接的路径
	语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
	例子:jdbc:mysql://localhost:3306/db3
	细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称		

在这里插入图片描述

② Connection 数据库连接对象

功能 :获取执行sql 的对象 + 管理事务

	public interface Connection extends Wrapper, AutoCloseable{
			Statement createStatement() throws SQLException
			//创建一个Statement对象,用于将SQL语句发送到数据库
			PreparedStatement prepareStatement(String sql) throws SQLException
			//创建一个PreparedStatement对象,用于将参数化的SQL语句发送到数据库,适用于多次相同的SQL语句执行
	}
  1. 管理事务:
    * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
    * 提交事务:commit()
    * 回滚事务:rollback()

③ Statement:执行sql的对象

功能 :增删改查

 	public interface Statement {
	   public interface PreparedStatement extends Statement{
	    	boolean execute(String sql) 
			//执行任何类型的SQL语句 
			int executeUpdate(String sql) 
			//执行DML(insert、update、delete)或 DDL(create,alter、drop)
			//返回值是影响行数,可以通过行数判断DML语句是否执行成功 返回值>0的则执行成功
			ResultSet executeQuery() 
			//执行DQL(select)查询语句,并返回查询 PreparedStatement的ResultSet对象 
	   ...
	   }
	}
execute任何语句
executeUpdateDML(insert、update、delete)或 DDL(create,alter、drop)
executeQueryDQL(select)
  • stmt 和 conn 要定义程全局变量,不然在try的作用域中定义,到 finally中就失效了
 		Statement stmt = null;
        Connection conn = null;
        try {         
            //Class.forName("com.mysql.jdbc.Driver");    
            String sql = "insert into account values(3,'王五',3000)";
            String sql  = "update account set balance = 1500 where id = 3";
            String sql  = "delete from account where id = 3";
            String sql  = "create table student (id int , name varchar(20))";
            
            //3.获取Connection对象
            conn = DriverManager.getConnection("jdbc:mysql:///day14", "root", "root");
            //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 e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //stmt.close();
            //7. 分别释放资源,避免空指针异常
            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

④ ResultSet:结果集对象

功能 :封装查询结果

	public interface ResultSet{
		boolean next() throws SQLException // 返回值:true 有数据,false没数据
		...
		String getString(int columnIndex) throws SQLException
		// int:代表列的编号,从1开始   如: getString(1)
		String getString(String columnLabel) throws SQLException
		// String:代表列名称, 如: getDouble("balance")

		int getInt(int columnIndex) throws SQLException
	}

在这里插入图片描述

  • 步骤:游标向下移动一行,判断是否有数据,获取数据
		Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");//1. 注册驱动         
            conn = DriverManager.getConnection("jdbc:mysql:///day14", "root", "root"); //2.获取连接对象
            String sql  = "select * from account";  //3.定义sql
            stmt = conn.createStatement(); //4.获取执行sql对象
            rs = stmt.executeQuery(sql); //5.执行sql
           
            while(rs.next()){ //获取数据            
                int id = rs.getInt(1);
                String name = rs.getString("name");
                double balance = rs.getDouble(3);
                System.out.println(id + "---" + name + "---" + balance);
            }
        
        catch...
        finally
            rs.close();               
            stmt.close();
            conn.close();

练习 :查询表的数据将其封装为对象,载入集合返回

	public List<Emp> findAll(){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        List<Emp> list = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///day14", "root", "root");
            String sql = "select * from emp";           
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            
            /******遍历结果集,封装对象,装载集合*******/
            Emp emp = null;
            list = new ArrayList<Emp>();
            
            while(rs.next()){
                //获取数据
                int id = rs.getInt("id");
                String ename = rs.getString("ename");
                int job_id = rs.getInt("job_id");
                int mgr = rs.getInt("mgr");
                Date joindate = rs.getDate("joindate");
                double salary = rs.getDouble("salary");
                double bonus = rs.getDouble("bonus");
                int dept_id = rs.getInt("dept_id");
                
                // 创建emp对象,并赋值
                emp = new Emp(); //复用
                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);
              
                list.add(emp);
            }
			
			catch... 
       		finally...         
               rs.close();
               stmt.close();          
               conn.close();

        return list;
    }

在这里插入图片描述

⑤ PreparedStatement:执行sql的对象

	 public interface PreparedStatement extends Statement

1) JDBC工具类

  • 用法展示
    public List<Emp> findAll2(){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        List<Emp> list = null;
        try {      
            conn = JDBCUtils.getConnection();//替换前面麻烦的写法          
            String sql = "select * from emp";          
            stmt = conn.createStatement();          
            rs = stmt.executeQuery(sql);
            ....                       
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,stmt,conn);
        }
        return list;
    }
  • Properties配置文件
	url=jdbc:mysql:///db3
	user=root
	password=root
	driver=com.mysql.jdbc.Driver
  • JDBC工具类
public class JDBCUtils {
    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("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties"));
            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);
    }

    /**
     * 释放资源
     * @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();
            }
        }
    }


    /**
     * 释放资源
     * @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();
            }
        }
    }

}

2) 登录练习

  1. 通过键盘录入用户名和密码
  2. 判断用户是否登录成功
  • 创建数据库表 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');
  • 测试主方法
		 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 flag = new JDBCDemo9().login(username, password);
			      
			   if(flag) 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 {
			      conn =  JDBCUtils.getConnection();    
			      String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
			       
			      stmt = conn.createStatement();
			      rs = stmt.executeQuery(sql);			        
			      return rs.next();//如果有下一行,则返回true
				  } 
			catch (SQLException e) {
			            e.printStackTrace();
			        }finally {
			            JDBCUtils.close(rs,stmt,conn);
			        }
			      return false;
			    }
		} 			        

3) Sql注入与解决

  • SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
	用户随便,输入密码:a' or 'a' = 'a
	sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' 
  • SQL注入解决:PreparedStatement对象,预编译的SQL:参数使用?作为占位符

步骤后期都会使用PreparedStatement来完成增删改查的所有操作

	1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
	2. 注册驱动
	3. 获取数据库连接对象 Connection
	4. 定义sql
		* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
	5. 获取执行sql语句的对象 PreparedStatement  Connection.prepareStatement(String sql) 
	6. 给?赋值:
		* 方法: setXxx(参数1,参数2)
			* 参数1:?的位置编号 从1 开始
			* 参数2:?的值
	7. 执行sql,接受返回结果,不需要传递sql语句
	8. 处理结果
	9. 释放资源
  • 登录方法,使用PreparedStatement实现
    public boolean login2(String username ,String password){
        if(username == null || password == null){
            return false;
        }
        //连接数据库判断是否登录成功
        Connection conn = null;
        PreparedStatement pstmt =  null;
        ResultSet rs = null;
        //1.获取连接
        try {
            conn =  JDBCUtils.getConnection();
            //2.定义sql
            String sql = "select * from user where username = ? and password = ?";
            //3.获取执行sql的对象
            pstmt = conn.prepareStatement(sql);
            //给?赋值
            pstmt.setString(1,username);
            pstmt.setString(2,password);
            //4.执行查询,不需要传递sql
            rs = pstmt.executeQuery();           
            return rs.next();//如果有下一行,则返回true

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,pstmt,conn);
        }
        return false;
    }

4) 事务控制

  • 事务:用事物管理一个包含多个步骤的业务操作,这些步骤要么同时成功,要么同时失败。
  • Connection对象管理事务
		在执行sql之前开启事务,开启事务:
		setAutoCommit(boolean autoCommit) //设置参数为false开启事务
			
		当所有sql都执行完提交事务
		commit() 
		
		catch中回滚事务
		rollback() 			
	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
            pstmt1.executeUpdate();
            // 手动制造异常
            int i = 3/0;

            pstmt2.executeUpdate();
            //提交事务
            conn.commit();
        } catch (Exception e) {
            //事务回滚
            try {
                if(conn != null) {
                    conn.rollback();
                }
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JDBCUtils.close(pstmt1,conn);
            JDBCUtils.close(pstmt2,null);
        }
    }
驱动加载成功 Thu Jun 08 22:43:18 CST 2023 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. java.sql.SQLException: No timezone mapping entry for 'GTM+8' at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:87) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:61) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:71) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:85) at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:862) at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:444) at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:230) at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:226) at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681) at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229) at com.til.jdbc.JDBC.main(JDBC.java:16) Caused by: com.mysql.cj.exceptions.WrongArgumentException: No timezone mapping entry for 'GTM+8' at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.ref
最新发布
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值