javaweb-day04-05-jdbc

今日内容–jdbc

  1. jdbc基本概念
  2. 入门小案例
  3. 四大核心类介绍
  4. sql注入
  5. 登录案例完善

一.jdbc基础

1.概念

  • 两个层面去理解jdbc
    • 干什么的?
    • 如何实现的?
1.全称:jdbc: java database connectivity  直译:java 数据库连接
2.表面意思:java连接并操作数据库 -- 解释jdbc是干什么的
3.本质含义:jdbc是sun公司制定的一套连接关系型数据库需要遵循的规范,即接口。而各个关系型数据库厂商自己来实现这个接口。程序员只需要面向接口编程。 -- 解释jdbc如何实现 连接并操作数据库 的
 
   即:jdbc通过定义了一些接口,借助接口中定义的抽象方法,来规定java要如何连接数据库,如何操作数据库。
而接口中的抽象方法的具体实现,会因为数据库的不同实现方式也不一样,但是这个不是我们关心的,因为这些实现类是数据库厂商提供的,我们只需会调用接口中的方法就可以了。
    所以:我们今天学习的内容并不是研究java到底如何连上数据库服务器的,这些底层也不是我们研究的范围,我们要学的就是一套操作步骤,学习在什么时候调用什么接口中的什么方法。

2.快速入门

  • 入门小案例
//1.导入jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");

//3.创建连接
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
System.out.println(con);

//4.定义sql
String sql="update account set balance=balance+500 where id=1";

//5.获取执行sql的对象 stmt
Statement stmt = con.createStatement();

//6.执行sql
int count = stmt.executeUpdate(sql);

//7.结果处理
System.out.println(count);

//8.释放资源
stmt.close();
con.close();
  • 注意:
1.得到了连接对象con,即意味着java程序和数据库服务器已经连通了。
2.stmt对象看似是执行sql,其实是发送sql给数据库服务器去执行
  • 总结流程:
1. 导入jar包
2. 加载驱动
3. 构建连接对象
4. 生成statement对象
5. 执行sql
6. 处理结果
7. 释放资源

3.核心类详解

3.1 DriverManager
  1. 两个功能

    1. 管理和注册驱动:查看源码解释
    2. 获取连接对象
  2. 获取连接对象的方法

    //1.通过 连接协议,用户名,密码 得到数据库连接对象
    Connection getConnection(String url,String user,String password)
    
    //2.通过 url,Properties 对象得到连接对象
     Connection getConnection(String url,Properties p)
    
  3. 连接数据库四大参数

    1.驱动类的字符串名:com.mysq.jdbc.Driver -- 在jar文件的META-INF/services文件夹下
    2.连接协议路径字符串url:不同的数据库url是不同的,mysql的写法
                jdbc:mysql://localhost:3306/数据库名[?参数名=参数值]
    3.数据库用户名 :root
    4.数据库登录密码:root
    
  4. url 是什么?

    • 是一个连接数据库的路径,用于标识数据库的位置

    • 包含:jdbc协议,mysql子协议,数据库所在主机名,数据库服务器的端口号,数据库名

    • 如果是本地服务器,端口号是3306,可简写:

      jdbc:mysql:///数据库名
      
  5. 乱码处理

    • 如果使用jdbc对数据库进行操作时出现中文乱码,可以在url后指定字符集编码参数,表示让数据库以UTF-8编码来处理数据

      jdbc:mysql:///数据库?characterEncoding=utf8
      
    • mysql8还需要指定时区

      jdbc:mysql:///数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
      
  6. 案例

    //1.使用用户名,密码,url得到连接对象
    public static void main(String [] args){
        String url="jdbc:mysql:///db1";
        Connection con=DriverManager.getConnection(url,"root","root");
        System.out.println(con);
    }
    
    //2.使用properties对象和url得到连接对象
    public static void main(String [] args){
        String url="jdbc:mysql:///db1";
        //将用户名和密码存进Properties对象中
        Properties info=new Properties();
        info.setProperty("user","root");
        info.setProperty("password","root");
        //使用url和Properties对象得到连接对象
        Connection con=DriverManager.getConnection(url,info);
        System.out.println(con);
    }
    //3.使用properties文件和url得到连接对象
    

    javaweb-day04-05-jdbc_第1张图片

@Test
    public void test1(){
        try {
            //注册驱动 Driver
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接数据库对象 DriverManager
            String url="jdbc:mysql://localhost:3306/db3 ?useSSL=false&serverTimezone=UTC";
            Connection con = DriverManager.getConnection(url, "root", "123456");
            System.out.println(con);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

javaweb-day04-05-jdbc_第2张图片

 @Test
    public void test2(){
        try {

            Class.forName("com.mysql.cj.jdbc.Driver");
            String url="jdbc:mysql://localhost:3306/db3 ?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";
            Properties p = new Properties();
            p.setProperty("user","root");
            p.setProperty("password","123456");
            Connection con = DriverManager.getConnection(url,p);
            System.out.println(con);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

javaweb-day04-05-jdbc_第3张图片
javaweb-day04-05-jdbc_第4张图片

@Test
    public void test3(){
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url="jdbc:mysql://localhost:3306/db3 ?useSSL=false&serverTimezone=UTC";
            InputStream is = Hello1.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties p = new Properties();
            p.load(is);
            Connection con = DriverManager.getConnection(url, p);
            System.out.println(con);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
3.2 Connection
  • Connection是一个接口,具体的实现类由数据库厂商提供实现,代表一个连接对象

  • 功能:

    1. 获取执行sql 的对象
    Statement createStatement()
    
    PreparedStatement prepareStatement(String sql)  //预编译对象,效率更高  
    
    1. 管理事务:
    - 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
    - 提交事务:commit() 
    - 回滚事务:rollback() 
    
3.3 Statement
  • 功能:用于发送sql语句给服务器,用于执行静态sql语句并返回结果

  • 方法

    //用于发送dml语句,执行增删改的操作,返回对数据库影响的行数,还可以执行ddl语句
    int executeUpdate(String sql) 
    
    //用于发送dql语句,执行查询的操作,返回结果集对象
    ResultSet executeQuery(String sql)
    
3.4 练习–dml和ddl
  • 演示代码

    • 增删改 – 返回受影响的行数
    • 创建student表 – 返回0
      javaweb-day04-05-jdbc_第5张图片
public class Hello2 {
    private Connection con;
    @Before //执行@Test前,先执行@Before
    public void init(){
        try {
            //注册驱动 Driver
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接数据库对象 DriverManager
            String url="jdbc:mysql://localhost:3306/db4 ?useSSL=false&serverTimezone=UTC";
             con = DriverManager.getConnection(url, "root", "123456");
         //   System.out.println(con);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test//dml 新增
    public void test1(){
        try {
            String sql="insert into dept values(50,'信息部','武汉')";
            Statement state = con.createStatement();
          // PreparedStatement ps = con.prepareStatement("insert into dept values(60,'信息部','武汉')");
            int i=state.executeUpdate(sql);
            System.out.println(i);//返回表格影响的行数 1
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Test//dml 删除
    public void test2(){
        try {
            String sql="delete from dept  where id='50'";
            Statement state = con.createStatement();
            int i=state.executeUpdate(sql);
            System.out.println(i);//返回表格影响的行数 1
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Test//dml 修改
    public void test3(){
        try {
            String sql="update dept set loc='武汉' where id='40'";
            Statement state = con.createStatement();
            int i=state.executeUpdate(sql);
            System.out.println(i);//返回表格影响的行数 1
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Test//ddl 修改表名
    public void test4(){
        try {
            String sql="alter table dept rename to dept1";
            Statement state = con.createStatement();
            int i=state.executeUpdate(sql);
            System.out.println(i);//  返回0
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
  • 注意:

    • 释放资源
    1.需要释放的对象 ResultSet Statement Connection
    2.释放原则:先开的后关,后开的先关 ResultSet --> Statement --> Connection
    3.放在finally代码块中
    
    finally {
        //6. 释放资源
    	//关闭之前要先判断
    		if (statement != null) {
    			try {
    				statement.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
3.5 ResultSet
  • 作用:封装数据库查询的结果集,并提供了遍历结果集的方法,取出每一条记录

  • 方法

    boolean next()  -- 每调用一次,行光标向下移动1行,返回true表示还有下一条记录,否则返回false
    
    数据类型 getXxx()  可以通过列名或列号获取某一行的某一列的数据
    
    • 画图理解行光标和遍历数据的步骤
  • 演示

    • 查询一行数据输出 if(rs.next())
    • 查询所有数据输出 while(rs.next())
3.6 练习–查询封装结果集
  • 代码演示–查询员工表,并封装结果集到list集合中

     /**
         * 查询所有emp对象
         * @return
         */
        public List<Emp> findAll(){
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            List<Emp> list = null;
            try {
                //1.注册驱动
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接
                conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
                //3.定义sql
                String sql = "select * from emp";
                //4.获取执行sql的对象
                stmt = conn.createStatement();
                //5.执行sql
                rs = stmt.executeQuery(sql);
                //6.遍历结果集,封装对象,装载集合
                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 (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (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 list;
        }
    
    
  • 注意事项

    • emp对象的创建要放在while循环的里面,因为每循环一次,相当于取出一行记录,封装到一个对象中。
    • 把emp对象存进list集合也是放在while循环里面
  • 核心步骤:

    1. 获得连接对象
    2. 执行查询
    3. 封装查询结果集

二.jdbc使用

1. 工具类

1.为什么要写工具类?
工具类的作用:
  1.实现注册驱动 和 连接对象的获取
  2.释放资源
  • 代码演示

    /**
     * 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){
            close(null,stmt,conn);//直接调用重载的close方法
        }
    
    
        /**
         * 释放资源
         * @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();
                }
            }
        }
    
    }
    
  • 注意事项

    • 资源用完后一定要释放
  • 可能出现的异常

    • 读取properties文件可能会出现异常

2. 登录案例

  • 代码演示

    /**
         * 登录方法
         */
        public boolean login(String username ,String password){
            if(username == null || password == null){
                return false;
            }
            //连接数据库判断是否登录成功
            Connection conn = null;
            Statement stmt =  null;
            ResultSet rs = null;
            //1.获取连接
            try {
                conn =  JDBCUtils.getConnection();
                //2.定义sql  获取username 和password两个数据
                String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
                System.out.println(sql);
                //3.获取执行sql的对象
                stmt = conn.createStatement();
                //4.执行查询
                rs = stmt.executeQuery(sql);
               
               return rs.next();//如果有下一行,则返回true
    
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JDBCUtils.close(rs,stmt,conn);
            }
    
    
            return false;
        }
    
  • 注意事项

    • 在sql语句拼接用户名和密码时注意单引号和双引号
  • 可能出现的异常

    • sql语句拼接不正确会导致sql语法异常

3. 预编译对象优化登录案例

  • 代码演示

    /**
         * 登录方法,使用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();
                //5.判断
                return rs.next();//如果有下一行,则返回true
    
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JDBCUtils.close(rs,pstmt,conn);
            }
    
            return false;
        }
    
  • 注意事项

    • 记得给占位符?赋值
  • 可能出现的异常

    • sql语句不要写错
  • 两种实现方式的差异:

1.sql语句不再是拼接参数 ,使用?代替参数
2.在创建stmt对象时,空参  ; 在创建pstm对象时,传入sql模板 --预编译
3.pstm对象还需要为?填充参数值
4.stmt对象执行sql时,需要传入sql ; pstm对象执行sql时,不用再传sql

优点:
1.PreparedStatement对象可以防止sql注入,Statement对象不可以
2.PreparedStatement对象是预编译方式执行sql,会提前检查sql是否有语法错误,因此执行效率更高

4. 管理事务

  • 事务的操作在jdbc流程中的位置
1.开启   con.setAutoCommit(false); 放在con对象创建后
2.回滚   con.rollback();  放在catch代码块
3.提交   con.commit();   try的最后一行
  • 代码演示

    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);
            }
        }
    
  • 注意事项

    • 要执行两个sql,所有需要创建两个prepareStatement对象,但只需一个连接对象即可
    • 两个prepareStatement对象最后都需要关闭
  • 可能出现的异常

    • sql语句语法错误会导致异常
    • 给两个prepareStatement对象中的sql模板的占位符赋值不要错乱

作业:

  1. 对一个表进行简单的增删改查

  2. 查询一个表中所有的数据,得到一个结果集,并在控制台全部输出

  3. 查询一个表中所有的数据,得到一个结果集,封装结果集到list集合,并在控制台输出

  4. 工具类的编写–读取外部的properties配置文件:1.获取连接对象 2.释放资源

  5. 用statement去实现登录案例

  6. 用子接口preparedstatement去优化登录案例

  7. 事务–转账案例

  8. 在控制台实现一个百里半用户管理系统,包含3个功能:

    1.用户登录 2.注册 3.用户查询。

    启动程序后,进入主菜单选项:

    输出:“请选择您要操作的功能:1.用户登录 2.新用户注册 3所有用户查询: ”

    功能说明:

    1.用户登录:

    ​ 请用户输入用户名和密码,接收后,去数据库的users表中查询是否存在该用户名。并输出合理的提示,例如:登录成功! 用户名不存在! 密码错误!

    ​ 不论登录是否成功,都返回主菜单界面。

    2.新用户注册 :

    ​ 请用户输入用户名和密码,接收后,去数据库的users表中执行新增操作。并输出合理的提示,例如:注册成功! 用户名已存在!

    3.所有用户查询:

    ​ 该功能必须在用户登录后方可查看,如果用户已经登录成功,即可在控制台查看所有用户的所有信息。如果没有登录,提示:您还没有登录呢,无权查看用户信息!

说明:

​ users表中只需要3列即可,id,name,password.

你可能感兴趣的:(Java,数据库,mysql)