java学习(16)-JDBC

本文详细解析了JDBC的工作原理,包括连接数据库的基本步骤、PreparedStatement与Statement的区别、事务控制及数据库连接池的使用,如Druid,并介绍了Spring JDBC的封装简化开发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



说明

因为是个人复习java的总结,所以结构稍显杂乱,有些语句过于口语化.
这部分内容准确地说不算是java的内容,而是为了JDBC进行的数据库复习


JDBC

  其实就是一个用于连接数据库的jar包.本质上说是一个连接数据库的接口,其中有不同数据库的实现类.能够实现任意数据库的使用.

  连接数据库的基本语句

import java.sql.*;

public class MysqlTest {
    public static void main(String[] args) {
        try {
			//导入jar包,反射中有提到
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try {
			//创建数据库的连接
            Connection connection = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/warehouse?serverTimezone=UTC"
					,"root"
					," ");

           	//sql语句
			String sql = "SELECT * FROM product";
            //获取数据库对象的statement
			Statement statement = connection.createStatement();
			//执行sql语句
            ResultSet result = statement.executeQuery(sql);

			//对结果集遍历输出
            while (result.next()){
                String number = result.getString(1);
                String name = result.getString(2);
                Date madeTime = result.getDate(3);
                double price = result.getDouble(4);
                System.out.println(number+name+madeTime+price);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

分析其中用到的类

  1. DriverManager
      其实就是驱动管理类,也就是我们到底使用的是什么数据库的驱动.我们通过第一句的Class.forName()加载了数据库具体的Driver类,但是其实加载Driver类的时候其中有静态代码块调用了DriverManager的registerDriver()方法,让驱动管理类知道调用的是什么驱动类.也就是说DriverManager其实是一个公用的类,而不同的是Driver驱动类.
      其实mysql5之后的jar包可以省略加载Driver类,会自动加载,但是建议书写

  2. Connection
      数据库连接类,其实就是通过驱动管理类使用getConnection()方法获取指定的数据库连接,其中参数:
    url: 指定连接的路径,基本语法为:jdbc:mysql://ip地址:端口号/数据库名称[?编码格式&&时区设置等 可选]
    user: 用户名
    password: 密码
      也可以将这些信息组合成一句作为参数,但是不太好阅读,另外url如果是本机的默认地址还可以直接跳过不书写.

  3. Statement
    执行sql的类,其中定义了执行sql语句的方法像:

Boolean execute(String sql )

  执行任意sql语句

int executeUpdate(String sql )

  执行DML和DDL语句(DML,DDL在上一篇数据库复习中有解释)

ResultSet executeQuery(String sql)

  执行DQL语句
  另外还有事务管理的相关语句

setAutoCommit(Boolean autoComic)

  开启事务,因为默认是自动提交,程序中需要设置其不提交从而达到开启事务的效果

commit()

  提交事务

rollBack()

  回滚事务

  1. ResultSet
      结果集类,其实就是用来封装查询到的数据库结果.
      其中获取内容实际上是通过移动光标+获取数据的方式实现的.
      也就是对于结果集,一开始有一个光标指向的是字段名,如果想要获取数据,就需要使用next()将光标向下移动,然后再使用getXXX()的方式获取指定指定字段的数据.具体方法就需要根据字段的数据类型来选择,其中传入的参数可以数字代表第几个字段,也可以传入字段名来表示获取的数据属于什么字段.

  2. PrepareStatement
      其实区别于Statement,这个就是预编译的执行SQL语句的类,也就是会现在数据库底层编译好需要执行的语句,只等待传入被占位符占据的数据.这种方式主要针对一些操作频繁的命令,因为底层编译好了,频繁使用时效率比较高

  具体使用大概如下所示:

String sql = “SELECT * FROM product WHERE number = ?;”
statement = connection.prepareStatement(sql);
statement.setString(1,1)
statement.executeQuery();



PrepareStatement和Statement的比较

  其实在编程中一般都是使用PrepareStatement,因为对于数据库的操作基本都是增删改查,操作都比较频繁,那么效率肯定比较高.像之后会写的jsp中,都是使用这种方式.

  另外Statement其实有一个很严重的问题,就是在解析语句的时候肯定会添加我们要求输入的内容,比如说登陆的时候.问题就处在这里,在获取输入之后就肯定会和sql语句拼接成一个字符串,那就意味着我们需要对传入的数据进行处理之后才能拼接字符串.
  因为万一输入的数据是代码,而我们只是简单地将其和字符串进行凭借,就会导致sql查询的内容出错,导致安全问题.

  虽然说对输入的内容去个符号也是可以的,但是比较麻烦.


JDBC简化案例

  从使用中可以看出,连接数据库的操作实际上挺繁琐的,而且连接数据库这个操作肯定需要经常进行,所以就可以思考将这个连接的操作给封装到一个类中,而我们只要调用这个类就可以反复进行数据库连接.那再思考,发现可能我们需要连接其他数据库进行操作,那又得改源代码,太麻烦.然后又想到之前使用properties来实现读取配置文件.这下豁然开朗,一个封装之后可以复用的代码.

  JDBC工具类

import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;

    static {
        try {
            //获取properties对象
            Properties properties = new Properties();

            //通过ClassLoader找到在src目录下的JDBC.properties的绝对路径,不需要自己去写路径.
            //Classloader会从当前程序的运行根目录开始寻找,然后返回一个绝对地址,所以只要放在src中就能找到
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            String path = classLoader.getResource("JDBC.properties").getPath();

            FileReader fileReader = new FileReader(path);
            properties.load(fileReader);

            //获取配置文件中的信息
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            String driver = properties.getProperty("driver");

            //加载驱动管理
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    //获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }


}

  测试类

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class MysqlTest {
    public static void main(String[] args) {
        try(
            Connection connection =JDBCUtils.getConnection();
            Statement statement = connection.createStatement();
        ){
            List<Emp> list = findAll(statement);
            System.out.println(list);
        }catch (SQLException sqlException){
            sqlException.printStackTrace();
        }
    }

    //将数据查询到list中
    public static List<Emp> findAll(Statement statement) throws SQLException {
        //查询的sql语句
        String sql = "SELECT * FROM product";
        //查询并获得结果集
        ResultSet result = statement.executeQuery(sql);

        List<Emp> list = new ArrayList<>();
        Emp emp = null;

        //读取结果集
        while (result.next()){
            emp = new Emp();
            String number = result.getString("number");
            String name = result.getString(2);
            Date madeTime = result.getDate(3);
            double price = result.getDouble(4);

            //设置自定义类型的属性
            emp.setMadeTime(madeTime);
            emp.setName(name);
            emp.setPrice(price);
            emp.setNumber(number);

            //将每条数据加到集合中
            list.add(emp);
        }
        return list;
    }
}

  自定义类型类

import java.util.Date;

/*
用于存放指定类型数据的类,在使用的时候具体改变这个类
 */
public class Emp {

    private String number;
    private String name;
    private double price;
    private Date madeTime;

    /*
    set,get,toString重写
     */
    public void setNumber(String number) {
        this.number = number;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void setMadeTime(Date madeTime) {
        this.madeTime = madeTime;
    }

    public String getNumber() {
        return number;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public Date getMadeTime() {
        return madeTime;
    }

    public String toString() {
        return "Emp{" +
                "number='" + number + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", madeTime=" + madeTime +
                '}';
    }

}

  细节上还存在一些小问题,但是大概就是这种形式,将前面的内容结合起来,加深了解.

  这里其实有一个问题没有解决,properties中的FileReader没有释放,需要手动释放吗,还是说等垃圾回收.


事务控制

  其实在代码中采用事务控制主要是为了防止异常的出现,如果没有采用事务,那么出现异常就会导致原本不应该生效的操作生效.
  所以事务的rollback()一般都是在catch到异常的时候进行的,然后在finally中commit().


数据库连接池

  其实就是一个存放数据库连接的容器,就跟线程池差不多,都是为了能够高效地需要连接地对象提供连接,而不用每次都重新创建连接,线程更高.

  数据库连接池使用的其实是api中的DtaSource接口,但是这个接口java没有去实现,而是留给了使用的人实现.其实有很多的厂商提供了开源的数据库连接池像C3P0(比较老),Druid(阿里实现,性能有保证)

  其实使用数据库连接池和直接创建连接调用方法上区别不是很大,都是通过getConnection()方法获取连接,然后使用close()归还连接,只是底层的源码对于连接的操作不同.


Druid

  安装去官网看一下就知道了,很简单.
  使用Druid的时候别忘了mysql的jar包
  使用语句如下,只写到获取连接,后面的操作没什么区别

  首先需要配置一个properties文件,因为数据库连接池中有很多属性可以配置,具体可以去查这里只是简单写了几个

  properties文件

driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/warehouse?serverTimezone=UTC
username = root
password = 94230nowhy

#初始化连接数量
initialSize = 5
#最大连接数
maxActive = 10
#最大等待时间
maxWait = 300

  测试类

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        //加载配置文件
        InputStream inputStream = Test.class.getResourceAsStream("druid.properties");
        properties.load(inputStream);
        //通过DruidDataSourceFactory获取连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //从连接池中获取一个连接
        Connection connection = dataSource.getConnection();
        //归还连接
        System.out.println(connection);

        //这个是工具类的测试
        Connection connection1 = JDBCUtils.getConnection();
        System.out.println(connection1);

    }
}

  工具类,也就是上面使用过的封装的连接

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class JDBCUtils {
    private static DataSource dataSource;

    static {
        Properties properties = new Properties();
        try {
            properties.load(JDBCUtils.class.getResourceAsStream("druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    public static DataSource getDataSource() {
        return dataSource;
    }

}

  细节上有很多问题,但是作为案例使用以下数据库连接池.


Spring JDBC

  是Spring框架中对于JDBC的封装,Spring会在之后学习中记录.
  Spring给封装之后的JDBC提供了很多的方法,能够方便的使用.简单地使用语句如下:

JdbcTemplate template = new JdbcTemplate(datasource);
String sql = “update emp set salary = 10000 where id = 1001;
//执行DML语句
template.update(sql);
//将查询到的结果封装成一个Map,采用预编译的形式只需要加个参数,需要注意结果只能一个
String sql1 = “select * from emp where id = ?;
template.queryForMap(sql,1);
//实际上都是和map一起使用,也就是List<Map<String,Object>>类型的list,Map存的是横向一条数据,list是纵向很多数据的集合.
String sql2 = “select * from emp”;
template.queryForList(sql2,1);
//结果封装为JavaBean对象
template.query(sql1,1)
//一般用于聚合函数的查询
String sql3 = “select count(id) from emp”;
template.queryForObject(sql3)

  这样就执行成功了,并且不需要释放资源,会自动释放
  然后datasource的获取结合上面的数据库连接池的工具类,那么就可以简化操作,甚至可以把连接写在静态代码块中.



如有错误欢迎读者批评指正!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值