javaweb之jdbc

jdbc概念

java database connectivity java数据库连接,java语言操作数据库

jdbc本质

是官方定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(jdbc)编程,但真正执行的代码是驱动jar包中的实现类

jdbc快速入门

步骤

  1. 导入驱动jar包(连接什么数据库就导入什么类型的jar包)
    1. 复制mysql-connector-java-5.1.39-bin.jar到项目libs目录下
    2. 右键libs --> Add as Library
  1. 注册驱动(告诉程序使用哪一个数据库的驱动jar包)
  2. 获取数据库连接对象 Connection(java代码与数据库之间的桥梁)
  3. 定义sql语句
  4. 获取执行sql语句的对象 Statement
  5. 执行sql,接收返回结果
  6. 处理结果
  7. 释放资源(先开的后关)

        //1.导入驱动jar包

       //2.注册驱动

       Class.forName("com.mysql.jdbc.Driver");

       //3.获取数据库连接对象

       Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");

       //4.定义SQL语句

       String sql="update hello set age=30 where id=1";

       //5.获取执行SQL的对象Statement

       Statement stmt = conn.createStatement();

       //6.执行SQL

       int result = stmt.executeUpdate(sql);

       //7.处理结果

       System.out.println(result);

       //8.释放资源

       stmt.close();

       conn.close();

详解各个对象

DriverManager:驱动管理对象

1.注册驱动

static void registerDriver(Driver driver)

我们写代码使用:Class.forName("com.mysql.jdbc.Driver");

查看com.mysql.jdbc.Driver类的源码发现静态代码块(当类被加载进内存,静态代码块会自动执行),本质上还是DriverManager.registerDriver(Driver driver),这样写会提高类的独立性且方便

static {

       try {

           DriverManager.registerDriver(new Driver());

       } catch (SQLException var1) {

           throw new RuntimeException("Can't register driver!");

       }

   }

注意:mysql5之后的驱动jar包可以省略注册驱动的步骤

2.获取数据库连接对象

方法:static Connection getConnection(String url, String user, String password)

参数:

  1. url:指定连接的路径语法:jdbc:mysql://ip地址(域名):端口/数据库名例子:jdbc:mysql://localhost:3306/contest注意
    1. 如果连接的是本机的mysql服务器,并且mysql服务端口为3306,则url可以简写为jdbc:mysql:///数据库名
    2. 在url后面设置编码,解决数据库中文乱码 jdbc:mysql:///contest?useUnicode=true&characterEncoding=UTF-8
  1. user:用户名
  2. password:密码

Connection:数据库连接对象

1.获取执行SQL的对象

Statement createStatement()

PreparedStatement prepareStatement(String sql)

2.管理事务

需要先把自动提交修改为手动提交

开启事务:setAutoCommit(boolean autoCommit) 调用该方法设置参数false(关闭自动提交),即开启事务

提交事务:commit()

回滚事务:rollback()

Statement:执行SQL的对象

1.执行SQL
  1. boolean execute(String sql):可以执行任意的SQL
  2. int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create,alter,drop)语句
    返回值:影响的行数。返回值>0则执行成功,反之则执行失败
  3. ResultSet executeQuery(String sql):执行DQL(select)语句
2.insert语句练习

        Connection coon=null;

       Statement stmt=null;

       try {

           Class.forName("com.mysql.jdbc.Driver");

           coon = DriverManager.getConnection("jdbc:mysql:///contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");

           String sql="insert into hello values(null,'张三',20)";

           stmt = coon.createStatement();

           int result = stmt.executeUpdate(sql);

           System.out.println(result);

           if (result>0){

               System.out.println("执行成功");

           }else{

               System.out.println("执行失败");

           }

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       } catch (SQLException throwables) {

           throwables.printStackTrace();

       }finally{

           /*避免空指针异常,因为try语句块中的每一句都有可能异常,那么stmt、coon会为null

           而且无论如何资源都要关闭,将其放在finally中*/

           if(stmt!=null){

               try {

                   stmt.close();

               } catch (SQLException throwables) {

                   throwables.printStackTrace();

               }

           }

           if (coon!=null){

               try {

                   coon.close();

               } catch (SQLException throwables) {

                   throwables.printStackTrace();

               }

           }

       }

ResultSet:结果集对象,封装着查询结果

boolean next():游标移动到下一行,并判断当前行是否有数据(最后一行之后),有数据返回true,否则返回false。游标默认在第一行之前

getXxx(参数):获取数据(从一行中获取某个列的数据)

  1. Xxx:代表数据类型 如:int getInt(),String getString()
  2. 参数:
    1. int:代表列的编号,从1开始 如:getString(1)
    2. String:代表列名称。如:getInt("age")

注意使用步骤:

  1. 游标向下移动一行
  2. 判断是否有数据
  3. 获取数据
select语句练习

数据库的表结构与java类相似

而表中的一行数据代表一个对象

定义一个方法,查询hello表的数据将其封装为对象,然后装载集合,返回。

  1. 定义Hello类
  2. 定义方法 public List<Hello> findAll(){}
  3. SQL语句 select * from hello;

//封装hello表数据的JavaBean

public class Hello {

   private int id;

   private String name;

   private int age;

   public int getId() {

       return id;

   }

   public void setId(int id) {

       this.id = id;

   }

   public String getName() {

       return name;

   }

   public void setName(String name) {

       this.name = name;

   }

   public int getAge() {

       return age;

   }

   public void setAge(int age) {

       this.age = age;

   }

   @Override

   public String toString() {

       return "Hello{" +

               "id=" + id +

               ", name='" + name + '\'' +

               ", age=" + age +

               '}';

   }

}

public class Jdbc {

   public static void main(String[] args){

       List<Hello> list = new Jdbc().findAll();

       System.out.println(list);

       System.out.println(list.size());

   }

   //查询所有Emp对象

   public List<Hello> findAll(){

       Connection coon=null;

       Statement stmt=null;

       ResultSet rs=null;

       List<Hello> list=null;

       try {

           Class.forName("com.mysql.jdbc.Driver");

           coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");

           stmt = coon.createStatement();

           String sql="select * from hello";

           rs = stmt.executeQuery(sql);

           list=new ArrayList<>();

           //遍历结果集,封装对象,装载集合

           Hello hello=null;

           while(rs.next()){

               int id = rs.getInt("id");

               String name = rs.getString("name");

               int age = rs.getInt("age");

               /**

                * 封装对象

                * Hello hello = new Hello();

                * 这里不使用上述代码,因为引用太多,浪费栈内存

                * 不如在外面写 Hello hello=null;

                * 再复用引用

                */

               hello=new Hello();

               hello.setId(id);

               hello.setAge(age);

               hello.setName(name);

               list.add(hello);

           }

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       } catch (SQLException throwables) {

           throwables.printStackTrace();

       }finally{

           if(rs!=null){

               try {

                   rs.close();

               } catch (SQLException throwables) {

                   throwables.printStackTrace();

               }

           }

           if(stmt!=null){

               try {

                   stmt.close();

               } catch (SQLException throwables) {

                   throwables.printStackTrace();

               }

           }

           if(coon!=null){

               try {

                   coon.close();

               } catch (SQLException throwables) {

                   throwables.printStackTrace();

               }

           }

       }

       return list;

   }

}

输出:

[Hello{id=1, name='ggbond', age=100}, Hello{id=2, name='??', age=20}, Hello{id=3, name='张三', age=20}]

3

抽取jdbc工具类:JDBCUtils

目的:简化书写

分析:

  1. 抽取注册驱动
  2. 抽取一个方法获取连接对象
    需求:不想传递参数(麻烦),还得保证工具类的通用性
    解决:配置文件(只需要改配置文件,代码不用动)
    jdbc.properties(在src目录下)

url=jdbc:mysql:///contest

user=xxx

password=xxx

driver=com.mysql.jdbc.Driver

  1. 抽取一个方法释放资源
JDBCUtils.java

public class JDBCUtils {

   private static String url;

   private static String user;

   private static String password;

   private static String driver;

   /**

    * 文件的读取只需一次

    * static{}静态代码块随着类被加载进内存自动执行,且只执行一次

    */

   static{

       //读取jdbc.properties配置文件,获取值

       try {

           //1.创建properties集合类

           Properties pro = new Properties();

           //获取src目录下文件的方式--> ClassLoader 类加载器(可以加载字节码文件进内存,并且可以获取src目录下资源的路径)

           ClassLoader classLoader = JDBCUtils.class.getClassLoader();

           URL res = classLoader.getResource("jdbc.properties");

           String path = res.getPath();

           //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 | ClassNotFoundException e) {

           e.printStackTrace();

       }

   }

   /**

    * 获取连接

    * 工具类方法为了方便使用一般都为静态方法

    * @return connection连接对象

    */

   public static Connection getConnection() throws SQLException {

       return DriverManager.getConnection(url, user, password);

   }

   /**

    * 释放资源

    * 释放资源有两种情况

    * 1.执行增删改操作,释放2个对象,statement对象,connection对象

    * 2.执行查询操作,释放3个对象,resultSet对象,statement对象,connection对象

    * 因此利用重载机制来实现

    */

   public static void close(Connection conn, Statement stmt){

       if(stmt!=null){

           try {

               stmt.close();

           } catch (SQLException throwables) {

               throwables.printStackTrace();

           }

       }

       if(conn!=null){

           try {

               conn.close();

           } catch (SQLException throwables) {

               throwables.printStackTrace();

           }

       }

   }

   public static void close(Connection conn, Statement stmt,ResultSet rs){

       if(rs!=null){

           try {

               rs.close();

           } catch (SQLException throwables) {

               throwables.printStackTrace();

           }

       }

       if(stmt!=null){

           try {

               stmt.close();

           } catch (SQLException throwables) {

               throwables.printStackTrace();

           }

       }

       if(conn!=null){

           try {

               conn.close();

           } catch (SQLException throwables) {

               throwables.printStackTrace();

           }

       }

   }

}

jdbc.java

public class Jdbc {

    public static void main(String[] args){

        List<Hello> list = new Jdbc().findAll();

        System.out.println(list);

        System.out.println(list.size());

    }

    //查询所有Emp对象

    public List<Hello> findAll(){

        Connection coon=null;

        Statement stmt=null;

        ResultSet rs=null;

        List<Hello> list=null;

        try {

            coon = JDBCUtils.getConnection();

            stmt = coon.createStatement();

            String sql="select * from hello";

            rs = stmt.executeQuery(sql);

            list=new ArrayList<>();

            //遍历结果集,封装对象,装载集合

            Hello hello=null;

            while(rs.next()){

                int id = rs.getInt("id");

                String name = rs.getString("name");

                int age = rs.getInt("age");

                /**

                 * 封装对象

                 * Hello hello = new Hello();

                 * 这里不使用上述代码,因为引用太多,浪费栈内存

                 * 不如在外面写 Hello hello=null;

                 * 再复用引用

                 */

                hello=new Hello();

                hello.setId(id);

                hello.setAge(age);

                hello.setName(name);

                list.add(hello);

            }

        } catch (SQLException throwables) {

            throwables.printStackTrace();

        }finally{

            JDBCUtils.close(coon, stmt, rs);

        }

        return list;

    }

}

PreparedStatement:执行SQL的对象

1.sql注入问题

在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

  1. 输入用户任意,输入密码:a' or 'a'='a
  2. sql:select * from user where username ='sadafs' and password = 'a' or 'a'='a'
2.使用PreparedStatement对象解决sql注入问题
  1. Statement对象执行静态的sql语句
  2. PreparedStatement对象执行预编译(动态)的sql语句
  3. 预编译的sql:参数使用?作为占位符
3.步骤
  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象 Connection
  4. 定义sql语句
    注意:sql的参数使用?作为占位符。如:select * from user where username =? and password = ?
  5. 获取执行sql语句的对象 PreparedStatement conn.preparedStatement(String sql)
  6. 给?赋值
    方法:setXxx(参数1,参数2)
    Xxx:数据类型
    参数1:?的位置编号。从1开始
    参数2:?的值
  7. 执行sql,接收返回结果。无需传递sql语句
  8. 处理结果
  9. 释放资源

注意:后期都会使用PreparedStatement来完成增删改查的所有操作

  1. 可以防止SQL注入
  2. 效率更高
示例代码

//查询数据库是否有某个用户,工具类JDBCUtils.java

public class Jdbc {

    public static void main(String[] args) {

        //键盘录入,接收name和age

        Scanner sc = new Scanner(System.in);

        System.out.println("请输入name");

        String name = sc.nextLine();

        System.out.println("请输入age");

        String age = sc.nextLine();

        //调用方法

        boolean flag = new Jdbc().find(name, age);

        //判断

        if (flag) {

            //存在

            System.out.println("存在用户");

        } else {

            System.out.println("不存在用户");

        }

    }


    public boolean find(String name, String age) {

        Connection coon = null;

        PreparedStatement pstmt = null;

        ResultSet rs = null;

        try {

            //获取连接

            coon = JDBCUtils.getConnection();

            //定义SQL

            String sql = "select * from hello where name =? and age = ?";

            //获取执行SQL的PreparedStatement对象

            pstmt = coon.prepareStatement(sql);

            //sql在执行前要给?赋值

            pstmt.setString(1, name);

            pstmt.setString(2, age);

            //执行查询,不需要传递SQL

            rs = pstmt.executeQuery();

            //判断

            return rs.next();

        } catch (SQLException throwables) {

            throwables.printStackTrace();

        } finally {

            JDBCUtils.close(coon, pstmt, rs);

        }

        return false;

    }

}

JDBC控制事务

1.事务

一个包含多个SQL语句的业务操作。如果这个业务操作被事务管理,里面的SQL语句要么同时成功,要么同时失败

2.操作

  1. 开启事务
  2. 提交事务
  3. 回滚事务

3.使用Connection对象来管理事务

  1. 开启事务:setAutoCommit(boolean autoCommit) 调用该方法设置参数false(关闭自动提交),即开启事务
    在执行SQL之前开启事务
  2. 提交事务:commit()
    当所有SQL都执行完提交事务
  3. 回滚事务:rollback()
    在catch中回滚事务(一旦有异常就会回滚)
上一篇:优先队列与二叉堆


下一篇:动态规划