JavaWeb基础 - 跟JDBC说拜拜!

大家好,我是一只学弱狗,记录学习的点点滴滴!

优质文章

优质专栏


谨以此篇博客,结束时长整8个月的寒暑假生活。

JDBC

概念

JDBC,是Java Database Connectivity的缩写,即Java数据库连接,先看搜狗百科上的解释:JDBC是一种用于执行SQL语句的Java API,由一组用Java语言编写的类和接口组成,它可以为多种关系型数据库提供统一访问,据此可以构建更高级的工具和接口,实现了所有这些面向标准的目标并且具有简单,严格类型定义且高性能实现的接口。
上面的解释我勉强接受,用简单的语言我们这样来描述:它是Sun公司定义的一套操作所有关系型数据库的规则,各个数据库厂商,像MySQL、SQL Server等等,他们自己去实现这些接口,提供数据库的驱动jar包,我们可以按照Sun公司的规则(接口)编程。

使用步骤

  1. 导入驱动jar包(或者使用maven构建)
  2. 注册驱动
  3. 获取数据库连接对象
  4. 定义sql语句
  5. 获取执行sql语句的对象
  6. 执行sql语句,接收返回结果
  7. 处理结果
  8. 释放资源

Maven依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>

代码演示

public static void main(String[] args) {
    
    

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        //1.导入jar包

        try {
    
    
            //2.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //3.获取连接对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "mysql");
            //4.定义sql语句
            String sql = "select * from tbl_user";
            //5.获取执行sql语句的对象
            statement = connection.createStatement();
            //6.执行sql语句
            resultSet = statement.executeQuery(sql);
            //7.处理结果
            while (resultSet.next()) {
    
    
                String username = resultSet.getString("username");
                String password = resultSet.getString("password");
                System.out.println(username + " : " + password);
            }
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //8.释放资源
            if (connection != null) {
    
    
                try {
    
    
                    connection.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (statement != null) {
    
    
                try {
    
    
                    statement.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (resultSet != null) {
    
    
                try {
    
    
                    resultSet.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }

相关对象详解

疑问猜测

看过了上面的代码,有小伙伴不禁会问,注册驱动,不就是通过反射加载Driver类进内存么,怎么就注册驱动了啊?
其实刚开始,我也有这样的疑问,不妨打开源码看一下
在这里插入图片描述噢,原来这儿是一个静态代码块,懂了懂了。

DriverManager

概念

驱动管理对象

功能
注册驱动

在Driver的静态代码块中,我们发现,通过DriverManager的静态方法registerDriver来注册的驱动

获取数据库连接
public static Connection getConnection​(String url,String user,String password)
  • url:指定连接路径
  • user:用户名
  • password:密码

Connection

概念

数据库连接对象

功能
获取执行sql的对象
Statement createStatement​() 	//执行静态sql
PreparedStatement prepareStatement​(String sql)	//执行预编译sql 
管理事务
//开启事务
void setAutoCommit​(boolean autoCommit) //调用该方法设置参数为false,即开启事务
//提交事务
void commit​() 
//回滚事务
void rollback​() 

Statement

概念

用于执行静态sql语句并返回其生成结果的对象

常用方法
int executeUpdate​(String sql) //执行增删改语句,返回影响的行数
ResultSet executeQuery​(String sql) //执行查语句,返回ResultSet结果集对象

ResultSet

概念

结果集对象,封装查询结果

常用方法
boolean next​() //游标向下移动一行,判断当前行是否是数据行,如果是数据行则返回true,否则返回false
XXX getXXX() //获取数据,该方法为重载方法,可以传入列数,也可以传入列名

案例:登陆验证功能

任何理论都离不开实践!
需求:用户在控制台输入用户名和密码,核对其正确性,整个过程存储为日志信息。

    public static void main(String[] args) {
    
    
        //待输入用户名
        String username = null;
        //待输入密码
        String password = null;
        //获取键盘输入流对象
        Scanner in = new Scanner(System.in);
        //日期对象
        Date date = new Date();
        //日期格式化对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
        //缓冲字符输出流
        BufferedWriter bw = null;
        //数据库连接对象
        Connection connection = null;
        //执行sql语句对象
        Statement statement = null;
        //结果集对象
        ResultSet resultSet = null;

        try {
    
    
            //获取类加载器
            ClassLoader classLoader = Demo.class.getClassLoader();
            //获取指定资源的URL路径
            URL resource = classLoader.getResource("conf/log.txt");
            //获取指定资源的绝对路径
            String path = resource.getPath();
            //文件字符输出流 追加字符
            FileWriter fw = new FileWriter(path, true);
            bw = new BufferedWriter(fw);

            System.out.println("用户名:");
            username = in.nextLine();
            System.out.println("密码:");
            password = in.nextLine();
            
            connection = JDBCUtils.getConnection();
            String sql = "select * from tbl_user where username = '" + username + "' and password = '" + password + "' ";
            System.out.println(sql);
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            String format = sdf.format(date);
            bw.newLine();
            bw.write("时间:" + format);
            bw.newLine();
            bw.write("用户名:" + username);
            bw.newLine();
            bw.write("密码:" + password);
            bw.newLine();
            if (resultSet.next()) {
    
    
                System.out.println("登录成功!");
                bw.write("登录成功!");
            } else {
    
    
                System.out.println("登录失败!");
                bw.write("登录失败!");
            }
            bw.newLine();
            bw.write("--------------------------------------");
        } catch (FileNotFoundException e) {
    
    
            System.out.println("未发现指定文件");
            System.exit(-1);
        } catch (SQLException e) {
    
    
            System.out.println("获取statement对象异常");
            System.exit(-1);
        } catch (IOException e) {
    
    
            System.out.println("IO读写异常");
            System.exit(-1);
        } finally {
    
    
            JDBCUtils.close(connection, statement, resultSet);
            try {
    
    
                bw.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

注意到,有个JDBCUtils类,我们看下这个类

public class JDBCUtils {
    
    
    private static String driver = null;
    private static String url = null;
    private static String user = null;
    private static String password = null;
    static {
    
    
        try {
    
    
            Properties properties = new Properties();
            /**
             * 获取src路径下的文件的方式-->ClassLoader 类加载器
             */
            properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/jdbc.properties"));
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            Class.forName(driver);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
    
    
        try {
    
    
            Connection connection = DriverManager.getConnection(url,user,password);
            return connection;
        } catch (SQLException e) {
    
    
            e.printStackTrace();
            return null;
        }
    }

    public static void close(Connection connection, Statement statement) {
    
    
        if (statement != null) {
    
    
            try {
    
    
                statement.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if(connection!=null){
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection connection, Statement statement, ResultSet resultSet){
    
    
        close(connection,statement);
        if(resultSet!=null){
    
    
            try {
    
    
                resultSet.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

仔细观察,提高了代码的复用性,通过配置文件修改数据库的属性,是不是值得我们学习呢?

案例思考

上面的代码,还是很不错的,但是也面临着一个问题,sql注入问题
在这里插入图片描述纳尼?不可思议,观察sql语句,对于聪明的你我就不做更多的解释了?如何解决?PreparedStatement,之前我们说过,它是一个执行预编译sql的对象,如何操作呢?
首先原先的步骤得先发生下改变

  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象 Connection
  4. 定义sql语句,注:sql的参数使用?来代替
  5. 获取执行sql语句的对象PreparedStatement
  6. 给占位符?赋值
  7. 执行sql语句,接收返回结果
  8. 处理结果
  9. 释放资源
    于是,我们有个加强版
    public static void main(String[] args) {
    
    
        String username = null;
        String password = null;
        Scanner in = new Scanner(System.in);
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
        BufferedWriter bw = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
    
    
            ClassLoader classLoader = DemoPlus.class.getClassLoader();
            URL resource = classLoader.getResource("conf/log.txt");
            String path = resource.getPath();
            FileWriter fw = new FileWriter(path, true);
            bw = new BufferedWriter(fw);
          /*  FileOutputStream fos = new FileOutputStream(path,true);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            BufferedWriter bw = new BufferedWriter(osw);*/

            System.out.println("用户名:");
            username = in.nextLine();
            System.out.println("密码:");
            password = in.nextLine();
            connection = JDBCUtils.getConnection();
            String sql = "select * from tbl_user where username = ? and password = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,username);
            preparedStatement.setString(2,password);
            resultSet = preparedStatement.executeQuery();
            String format = sdf.format(date);
            bw.newLine();
            bw.write("时间:" + format);
            bw.newLine();
            bw.write("用户名:" + username);
            bw.newLine();
            bw.write("密码:" + password);
            bw.newLine();
            if (resultSet.next()) {
    
    
                System.out.println("登录成功!");
                bw.write("登录成功!");
            } else {
    
    
                System.out.println("登录失败!");
                bw.write("登录失败!");
            }
            bw.newLine();
            bw.write("--------------------------------------");
        } catch (FileNotFoundException e) {
    
    
            System.out.println("未发现指定文件");
            System.exit(-1);
        } catch (SQLException e) {
    
    
            System.out.println("获取statement对象异常");
            System.exit(-1);
        } catch (IOException e) {
    
    
            System.out.println("IO读写异常");
            System.exit(-1);
        } finally {
    
    
            JDBCUtils.close(connection, preparedStatement, resultSet);
            try {
    
    
                bw.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

我们再来sql注入一次,没门。。。
在这里插入图片描述

事务管理及演示

什么是事务?

指一个包含多个步骤的业务操作,如果这个业务被事务管理,则这多个步骤要么同时执行成功,要么同时失败。

模拟:转账

构建数据库表
在这里插入图片描述
我们的需求是张三给李四转1000元,期望结果张三余额是4000元,李四余额是6000元。

    public static void main(String[] args) {
    
    
        //获取数据库连接对象
        Connection connection = JDBCUtils.getConnection();

        try {
    
    
            //开启事务
            connection.setAutoCommit(false);
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
        //定义sql语句
        String sql = "update tbl_account set balance = balance + ? where id = ? ";
        //获取执行预编译sql语句的PreparedStatement对象
        PreparedStatement preparedStatement1 = null;
        PreparedStatement preparedStatement2 = null;
        try {
    
    
            preparedStatement1 = connection.prepareStatement(sql);
            preparedStatement1.setDouble(1,-1000);
            preparedStatement1.setInt(2,1);
            preparedStatement2 = connection.prepareStatement(sql);
            preparedStatement2.setDouble(1,+1000);
            preparedStatement2.setInt(2,2);

            int count1 = preparedStatement1.executeUpdate();

            //此处发生错误
            int a = 3/0;

            int count2 = preparedStatement2.executeUpdate();

            //提交事务
            connection.commit();

            if(count1>0 && count2>0){
    
    
                System.out.println("转账成功!");
            }else{
    
    
                System.out.println("转账失败!");
            }
        } catch (SQLException e) {
    
    
            try {
    
    
                //事务回滚
                connection.rollback();
            } catch (SQLException ex) {
    
    
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
    
    
            JDBCUtils.close(connection,preparedStatement1);
            JDBCUtils.close(null,preparedStatement2);
        }
    }

以上代码,感兴趣慢慢研究,如果去掉了事务管理,会怎么样呢?

数据库连接池

概念

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

剖析

Java官方提供了DataSource接口,该接口由数据库厂商实现,用于构建数据库连接池,可以使用其方法来过去连接对象或者归还连接

C3P0数据库连接池

使用步骤

  1. 导入jar包
  2. 编写配置文件(注:文件必须是c3p0.properties或c3p0-config.xml)
  3. 通过ComboPooledDataSource 创建DataSource对象
  4. 获取连接Connection对象
  5. 编写sql语句,执行并处理结果
  6. 归还连接对象

配置文件

<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property>
        <property name="user">root</property>
        <property name="password">mysql</property>
        <!-- 初始化申请的连接数量 -->
        <property name="initialPoolSize">10</property>
        <!-- 最大的连接数量 -->
        <property name="maxPoolSize">10</property>
        <!-- 超时时间 -->
        <property name="checkoutTimeout">3000</property>
    </default-config>

    <!-- This app is massive! -->
    <named-config name="intergalactoApp">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property>
        <property name="user">root</property>
        <property name="password">mysql</property>
        <!-- 初始化申请的连接数量 -->
        <property name="initialPoolSize">10</property>
        <!-- 最大的连接数量 -->
        <property name="maxPoolSize">10</property>
        <!-- 超时时间 -->
        <property name="checkoutTimeout">3000</property>
    </named-config>
</c3p0-config>

public static void main(String[] args) {
    
    

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
    
    
            //1.导入jar包
            //2.编写配置文件
            //3.通过ComboPooledDataSource创建DataSource对象
            DataSource dataSource = new ComboPooledDataSource();
            //4.获取连接对象
            connection = dataSource.getConnection();
            //5.编写sql语句,执行并处理结果
            String sql = "select * from tbl_account";
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
    
    
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                double balance = resultSet.getDouble("balance");
                System.out.println(id + " : " + name + " : " + balance);
            }
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (resultSet != null) {
    
    
                try {
    
    
                    resultSet.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
    
    
                try {
    
    
                    preparedStatement.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (connection != null) {
    
    
                try {
    
    
                    //6.归还连接对象
                    connection.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
        }

    }

Druid数据库连接池

使用步骤

  1. 导入jar包
  2. 编写配置文件(注:不同于c3p0,该配置文件可自定义名称及位置,通过properties来读取它)
  3. 通过DruidDataSourceFactory 创建DataSource对象
  4. 获取连接Connection对象
  5. 编写sql语句,执行并处理结果
  6. 归还连接对象

配置文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=mysql
maxActive=10
maxWait=3000


public class JDBCUtils {
    
    
    private static DataSource dataSource = null;
    static {
    
    
        try {
    
    
            Properties properties = new Properties();
            properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    public static DataSource getDataSource(){
    
    
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
    
    
        return dataSource.getConnection();
    }

    public static void close(Connection connection, Statement statement) {
    
    
        if (statement != null) {
    
    
            try {
    
    
                statement.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if(connection!=null){
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection connection, Statement statement, ResultSet resultSet){
    
    
        close(connection,statement);
        if(resultSet!=null){
    
    
            try {
    
    
                resultSet.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

Spring JDBC

概念

Spring框架对JDBC的简单封装,简化JDBC的开发

常用方法

  • 创建JdbcTemplate的对象,参数为一个数据库连接池DataSource对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());
  • 使用update方法来实现增删改,返回影响的行数
	String sql = "update tbl_account set balance = ? where id = ?";
    int count = jdbcTemplate.update(sql, balance, id);
	String sql = "insert into tbl_account values(null,?,?)";
    int count = jdbcTemplate.update(sql, name, balance);
	String sql = "delete from tbl_account where id = ?";
    int count = jdbcTemplate.update(sql, id);
  • 使用queryForMap 方法来返回数据,注:该查询的数据集的长度只能是1,列名作为key,值为value
	String sql = "select * from tbl_account where balance = ?";
	Map<String, Object> map = jdbcTemplate.queryForMap(sql,balance);
  • 使用queryForList 方法来返回数据集,注:该方式是先将每条数据封装为Map集合,再装载到List集合中
	String sql = "select * from tbl_account";
    List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
  • 使用queryForObject 方法来返回指定数值,注:该方式一般用于聚合函数的查询
	String sql = "select count(*) from tbl_account";
    Long total = jdbcTemplate.queryForObject(sql, long.class);
  • 使用query 方法来返回指定对象的数值

**方式一:**自己写RowMapper接口的方法
**方式二:**使用BeanPropertyRowMapper实现类

	String sql = "select * from tbl_account";
        /*List<Account> list = jdbcTemplate.query(sql,new RowMapper<Account>(){
            @Override
            public Account mapRow(ResultSet resultSet, int i) throws SQLException {
                Account account = new Account();
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                double balance = resultSet.getDouble("balance");
                account.setId(id);
                account.setName(name);
                account.setBalance(balance);
                return account;
            }
        });*/
  List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));

猜你喜欢

转载自blog.csdn.net/qq_44486437/article/details/108424662