自学内容网 自学内容网

Java - JDBC

JDBC

JDBC简介

为了便于程序保存和读取数据,而且能直接通过条件快速查询大指定的数据,就出现了数据库(Datebase)这种专门用于集中存取和查询的软件。

JDBC是Java程序访问数据库的标准接口。

使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问数据库的,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。

JDBC接口是Java标准库自带的,具体的JDBC驱动是由数据库厂商提供的 从代码上来看,JDBC就是定义了一组接口,某个具体的JDBC驱动就是实现这些JDBC接口的类。

MySQL的JDBC驱动就是一个jar包,它本身也是纯Java编写的。我们只需要引入这个JDBC驱动的jar包就可以正常访问MySQL服务器。

我们自己编写的代码只需要引用Java标准库提供的java.sql包下面的相关接口,就可以间接地通过JDBC驱动jar包,通过网络,来访问MySQL服务器了,所有的复杂的网络通讯也都被封装到了JDBC驱动中。

现在Maven的依赖最新版本号是8.0.33。

</dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
            <scope>runtime</scope>
</dependency>

JDBC连接

Connection

Connection代表一个JDBC连接,它相当于Java程序到数据库的连接(通常是TCP连接)。

打开一个Connection时,需要准备URL、用户名和密码,才能成功连接到数据库。

核心代码

核心代码是DriverManager提供的静态方法getConnection()。

DriverManager会自动扫描classpath,找到所有的JDBC驱动,然后根据我们传入的URL自动挑选一个合适的驱动。

因为JDBC连接是一种昂贵的资源,所以使用后要及时释放,所以使用try (resource)来自动释放。

class Main{
    public static void main(String[] args) throws SQLException {
        String JDBC_URL = "jdbc:mysql://localhost:3306/learnjdbc?useSSL=false&serverTimezone=UTC";//要连接已创建的数据库,我这个test数据库是已经创建好的。
        String JDBC_USER = "root";//root是默认的超级用户
        String JDBC_PASSWORD = "11111111";//自定义密码,千万设置完别忘了
        Connection conn = null;
        try {
            // 获取连接
            conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            System.out.println("连接成功!");

            // TODO: 访问数据库...

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDBC查询

代码步骤:

1.通过Connection提供的createStatement()方法创建一个Statement对象,用于执行一个查询;

2.执行Statement对象提供的executeQuery("SELECT * FROM students")并传入SQL语句,执行查询并获得返回的结果集,使用ResultSet来引用这个结果集;

3.反复调用ResultSet的next()方法并读取每一行结果。

注意:

因为Connection、Statement、ResultSet都是许哟啊关闭的资源,所以需要嵌套使用try,确保即时关闭,所以显得繁琐。

ResultSet获取列时,是从1开始的。

ResultSet获取列时,必须根据select的列对应的位置调用getLong()、getString()这些方法,否则对应位置的数据类型不对,将报错。

        Connection conn = null;
        try {
            // 获取连接
            conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            System.out.println("连接成功!");

            // TODO: 访问数据库...
            Statement statement=null;
            try {
                statement = conn.createStatement();
                ResultSet result=null;
                try {
                    result = statement.executeQuery("select * from students");
                    while (result.next()){
                        long id=result.getLong(1);
                        String name=result.getString(2);
                        long gender=result.getLong(3);
                        long grade=result.getLong(4);
                        int score=result.getInt(5);
                        System.out.println(id+" "+name+" "+gender+" "+grade+" "+score);
                    }
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                } finally {
                    if (result!=null)
                        try {
                            result.close();
                        } catch (SQLException e) {
                            throw new RuntimeException(e);
                        }
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } catch (RuntimeException e) {
                throw new RuntimeException(e);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

SQL注入

为了避免SQL注入攻击,和statement拼接字符串非常容易引发SQL注入问题。

所以使用Java对数据库进行操作时,必须使用PreparedStatement,严禁任何通过参数拼字符串的代码!!!!

            // TODO: 访问数据库...
            PreparedStatement ps=null;
            ps = conn.prepareStatement("select * from students where id>? and score>=?");
            ps.setObject(1,2);
            ps.setObject(2,60);
            ResultSet result=null;
            result=ps.executeQuery();
            while (result.next()){
                long id=result.getLong(1);
                String name=result.getString(2);
                long gender=result.getLong(3);
                long grade=result.getLong(4);
                int score=result.getInt(5);
                System.out.println(id+" "+name+" "+gender+" "+grade+" "+score);
            }

数据类型

使用JDBC的时候,我们需要在Java数据类型和SQL数据类型之间进行转换。 JDBC在java.sql.Types定义了一组常量来表示如何映射SQL数据类型。

JDBC的增删改查

// TODO: 访问数据库...
PreparedStatement ps=null;
            ps = conn.prepareStatement("select * from students where id>? and score>=?");
            ps.setObject(1,2);
            ps.setObject(2,60);
            ResultSet result=null;
            result=ps.executeQuery();
            while (result.next()){
                long id=result.getLong(1);
                String name=result.getString(2);
                long gender=result.getLong(3);
                long grade=result.getLong(4);
                int score=result.getInt(5);
                System.out.println(id+" "+name+" "+gender+" "+grade+" "+score);
            }

// TODO: 访问数据库...
            PreparedStatement ps=null;
            ps=conn.prepareStatement("INSERT INTO students (grade, name, gender,score) VALUES (?,?,?,?)",Statement.RETURN_GENERATED_KEYS);
            ps.setObject(1, 1); // grade
            ps.setObject(2, "olderhard"); // name
            ps.setObject(3, 1); // gender
            ps.setObject(4, 90); // score
            int n = ps.executeUpdate();
            ResultSet result=null;
            result=ps.getGeneratedKeys();
            if (result.next()){
                System.out.println(result.getLong(1));
            }

// TODO: 访问数据库...
            PreparedStatement ps=null;
            ps=conn.prepareStatement("UPDATE students SET name=? WHERE id=?");
            ps.setObject(1,"bob");
            ps.setObject(2,14);
            iint n=ps.executeUpdate();

// TODO: 访问数据库...
            PreparedStatement ps=null;
            ps=conn.prepareStatement("DELETE FROM students WHERE id=?");
            ps.setObject(1,15);
            int n=ps.executeUpdate();

JDBC事务

数据库事务时由若干个SQL语句构成的一个操作序列。数据库系统保证在一个事务中所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性,原子性,一致性,隔离性,持久性。

要在JDBC中执行事务,本质上就是如何把多条SQL包裹在一个数据库事务中执行。

注意关键点即可:连接成功后;conn.setAutoCommit(false);之后对数据库操作完成后;就conn.commit(),提交事务;再加一个异常回滚conn.rollback()。

Connection conn = null;
        PreparedStatement p1=null;
        PreparedStatement p2=null;
        try {
            // 获取连接
            conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            System.out.println("连接成功!");
            conn.setAutoCommit(false);  //关闭自动提交模式

            // TODO: 一系列对数据库的操作...

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

        } catch (SQLException e) {
            if(conn!=null){
                try {
                    conn.rollback();
                    System.out.println("Transaction rolled back.");
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }

JDBC批处理

在JDBC代码中,我们可以利用SQL数据库的特性,把同一个SQL但参数不同的若干次操作合并为一个batch执行。

使用JDBC的batch操作会大大提高执行效率,对内容相同,参数不同的SQL,要优先考虑batch操作。

// TODO: 访问数据库...
            PreparedStatement ps=null;
            ps=conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)");
            for (Student s : students) {
                ps.setString(1, s.name);
                ps.setBoolean(2, s.gender);
                ps.setInt(3, s.grade);
                ps.setInt(4, s.score);
                ps.addBatch(); // 添加到batch
            }
            // 执行batch:
            int[] ns = ps.executeBatch();
            for (int n : ns) {
                System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量
            }

JDBC连接池

与多线程的线程池原因类似,在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接。那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池复用已经创建好的连接。

JDBC连接池有一个标准的接口javax.sql.DataSource,要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。

常用的JDBC连接池有:HikariCP、C3P0、BoneCP、Druid

之后我们通过创建一个DATa Source实例(对象),这个就是连接池。

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("password");
config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒
config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10
DataSource ds = new HikariDataSource(config);

如何使用连接池?

类似这个

conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);

这样使用

conn = config.getConnection();

原文地址:https://blog.csdn.net/m0_74403543/article/details/142705633

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!