JDBC编程


一、最简单的流程

  • 加载相应的数据库驱动
  • 通过DriverManger获取Connection
  • 通过Connection获取PrepareStatement,并设置查询语句
  • 通过PrepareStatement执行sql语句
  • 通过ResultSet获取查询结果

例子:

import java.sql.*;

public class Main {

    public static void main(String[] args) {
        System.out.println("打印出test数据库中的user表数据");
        //1.加载数据库驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到mysql数据库驱动!!!");
            e.printStackTrace();
            System.exit(0);
        }
        //2.获取连接
        Connection conn;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");

            //3.获取PrepareStatement对象,并传入查询语句
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
            //4.执行PrepareStatement对象
            ResultSet resultSet = ps.executeQuery();
            //5.用游标输出结果
            while (resultSet.next()) {
                System.out.print("id:" + resultSet.getInt(1));
                System.out.print("   username:" + resultSet.getString(2));
                System.out.println("   age:" + resultSet.getInt(3));

            }
        } catch (SQLException e) {
            e.printStackTrace();
            System.exit(0);
        }
    }
}

运行结果:
在这里插入图片描述


二、常用接口(还真是Interface)

接口说明
DriverManger数据库驱动程序,用于连接jdbc和对应数据库
Connection用于创建一个数据库连接,要想访问数据库,就得先获取数据库连接。通过DrverManger.getConnection()获取连接
PrepareStatement预编译的Statement对象,只有更改sql参数就行。通过ConnectionInstance.prepareStatement()获取。
ResultSet结果集对象。可以同PrepareStatementInstance.executeQuery()返回

三、执行SQL语句的几种方式

方式说明
execute()此方法的返回值是boolean,表示是否返回ResulSet对象。若是,可以通过getResultSet()获取。
executeUpate()返回受影响的行数。
executeLargeUpdate()和executeUpdate()差不多,只是当受影响的数目非常大时(推荐使用executeLargerUpdate())
executeQuery()只能执行查询语句,返回ResultSet

上面方法时Statement的,当然PrepareStatement也提供了这些方法。但使用Statement和PrepareStatement的区别:

  1. PrepareStatement预编译SQL语句,性能更好
  2. PrepareStatement不用拼接sql语句,编程更简单
  3. PrepareStatement可以防止SQL注入(2就是原因),更safe。

使用CallableStatement可以调用存储过程,可以通过conn.prepareCall(格式)获取:

调用格式是{call 过程名(?,?,?)}。其中?表示占位符。
‘?’表示的参数可以是传入参数,也可以是传出参数:

传入参数:通过setXXX(position,value)为参数设置
传出参数:需要registerOutParmter(position,TYPE)来注册

例子(我已经通过navicat创建了一个存储过程):
在这里插入图片描述

/**
     * 使用CallableStatement调用存储过程
     */
    public static void callableStatementTest(){
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            CallableStatement cs=conn.prepareCall("{call add_pro(?,?,?)}");
            cs.setInt(1,1);
            cs.setInt(2,2);
            cs.registerOutParameter(3,Types.INTEGER);
            cs.execute();
            System.out.println(cs.getInt(3));

        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

在main函数调用后的运行结果:
3


四、ResultSet管理结果集

4.1 可滚动、可更新的结果集

可滚动是指游标可以定位到任意位置(通过absolute()、previous()等方法)。我们默认创建的ResultSet是不可以修改的。所以要创建可滚动、可更新的结果集方法:

  1. 在获取PrepareStatement或Statement时,要传入参数(TYPE_SCROLL_INSENSTIVE,CONCUR_UPDATEABLE,其他参数我没写)。例子在这里插入图片描述
  2. 在更改ResultSet后(ResultSet提供了updateXXX(index,value)修改结果集),要调用updateRow()方法

4.2 处理Blob类型(二进制)数据

Blob类型使用来存储图片、声音等大型文件的。虽然不推荐直接将图片、声音等大型文件直接存在数据库,但是数据库任然提供了存储这些大型文件的方法

  • PrepareStatement提供了setBinaryStrem(int parameterIndex,InputStream in)方法来存储二进制文件
  • ResultSet提供了getBlob(int index)方法获取Blob对象,Blob对象提供了getBinaryStream()方法获取输出流。Blob对象也提供了getBytes()方法获取对应的二进制数组。

4.3 通过ResultSetMetaData分析结果集

ResultSet提供了getResultSetMetaData()方法来获取ResultSetMetaData对象,有了它,我们就可以获取ResultSet的描述信息了。ResultSetMetaData的常用方法:
在这里插入图片描述

需要注意的是使用ResultSetMetaData需要一定的系统开销。


五、RowSet管理结国集和分析结果集

5.1 什么是RowSet?

个人认为RowSet是对ResultSet的一种改善。它默认是可滚动的、可更新的可序列化的结果集。对于离线RowSet来说,创建时已经将数据读入内存,所以无需与数据库保持连接,从而降低了数据库的负载,提高了性能。RowSet规范的接口类图如下:
在这里插入图片描述


5.2 怎样创建RowSet?

老版(不推荐使用):

通过JdbcRowSetImpl,CacheRowSetImpl,WebRowSetImpl,JoinRowSet,FilterRowSet创建。

新版(RowSetProvider、RowSetFactory):
在这里插入图片描述


5.3 一个例子离线RowSet及离线RowSet的查询分页


    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        CachedRowSet crs=simpleStep();
        //对crs进行操作
        while(crs.next()){
            System.out.println("id:"+crs.getInt(1)+"   username:"+crs.getString(2)+"   age:"+crs.getInt(3));
            //修改年龄,年龄加1
            crs.updateInt(3,crs.getInt(3)+1);
            crs.updateRow();
        }
        //重新获取连接,完成更新
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
        conn.setAutoCommit(false);
        crs.acceptChanges(conn);
    }

    
    public static CachedRowSet simpleStep(){
        System.out.println("打印出test数据库中的user表数据");
        //1.加载数据库驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到mysql数据库驱动!!!");
            e.printStackTrace();
            System.exit(0);
        }
        //2.获取连接
        Connection conn;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");

            //3.获取PrepareStatement对象,并传入查询语句
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
            //4.执行PrepareStatement对象
            ResultSet resultSet = ps.executeQuery();

            //通过离线RowSet存
            RowSetFactory rsf= RowSetProvider.newFactory();
            CachedRowSet crs=rsf.createCachedRowSet();
            //5.用游标输出结果
            /*while (resultSet.next()) {
                System.out.print("id:" + resultSet.getInt(1));
                System.out.print("   username:" + resultSet.getString(2));
                System.out.println("   age:" + resultSet.getInt(3));

            }*/
            crs.populate(resultSet);//将resultSet封装起来
            return crs;
        } catch (SQLException e) {
            e.printStackTrace();
            System.exit(0);
        }
        return null;
    }

运行结果:
在这里插入图片描述
注意:populate进行封装时,默认应该是有一个开始位置的!!!!

分页查询,只是在上面代码基础上进行简单的修改。为什么要进行分页查询?是因为CacheRowSet是保存在内存中的,当记录数太多时会占有很大内存,所以进行分页查询。

 public static void main(String[] args) throws SQLException, ClassNotFoundException {
        CachedRowSet crs=simpleStep(1,2);
        //对crs进行操作
        System.out.println( crs.absolute(2));
        System.out.println(crs.absolute(1));
        while(crs.next()){
            System.out.println("id:"+crs.getInt(1)+"   username:"+crs.getString(2)+"   age:"+crs.getInt(3));
            //修改年龄,年龄加1
            crs.updateInt(3,crs.getInt(3)+1);
            crs.updateRow();
        }
        //重新获取连接,完成更新
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
        conn.setAutoCommit(false);
        crs.acceptChanges(conn);
    }


    public static CachedRowSet simpleStep(int pageIndex,int pageSize){
        System.out.println("打印出test数据库中的user表数据");
        //1.加载数据库驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到mysql数据库驱动!!!");
            e.printStackTrace();
            System.exit(0);
        }
        //2.获取连接
        Connection conn;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");

            //3.获取PrepareStatement对象,并传入查询语句
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
            //4.执行PrepareStatement对象
            ResultSet resultSet = ps.executeQuery();

            //通过离线RowSet存
            RowSetFactory rsf= RowSetProvider.newFactory();
            CachedRowSet crs=rsf.createCachedRowSet();
            
            crs.setPageSize(pageSize);
            crs.populate(resultSet,(pageIndex-1)*pageSize+1);
            return crs;
        } catch (SQLException e) {
            e.printStackTrace();
            System.exit(0);
        }
        return null;
    }

运行结果:
在这里插入图片描述


六、事务管理与批量更新

6.1 mysql的事务支持

事务提交的方式:

显示提交:自动提交
自动提交是和开启事务相反的,开启事务就关闭了自动提交,开启自动提交就关闭了事务。

事务回滚方式:

在这里插入图片描述

如果只想暂时性开启事务,可以使用start transaction或begin两个命令。
还有,mysql 提供了 savepoint 来设置事务中间点,这样rollback时就可以选择回到中间点了。


6.2 jdbc的事物支持

Connection默认是打开自动提交,关闭事务的。所以可以通过==setAutoCommit()==来关闭自动提交,打开事务。
提交事务使用conn.commit()。
事务回滚使用conn.rollback()。
Connection也提供了设置中间点的方法setSavepoint(),当然也提供了相应的回滚方法rollback(savapoin)。


6.3 批量更新

Statement或PrepareStatement提供了addBatch(sql),executeLargeBatch()方法来支持批量更新


分析数据库信息

可以通过 DatabaseMetaData 来分析数据库信息,DatabaseMetaData 以ResultSet来返回查询信息。
还可以通过 系统表 来分析数据库。


使用连接池管理连接

为什么要使用连接池管理连接?是因为单独每一次连接建立、关闭都很耗费资源,对系统性能影响尤为明显。
数据库连接池的常用参数:

在这里插入图片描述

可以使用DBCP,C3P0数据源1来管理连接。


  1. DataSource称为数据源,它包括连接池和连接池管理两部分。但习惯上把DataSource称为连接池。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值