0.JDBC的基础知识

0.0 DBUtils 与 数据库连接池
0.0.1 DBUtils
Dbutils是一个对JDBC进行简单封装的开源工具类库,在学完JDBC基础后可以使用DBUtils编写一个JDBC的工具类,这样的封装可以在以后直接调用工具类来操作JDBC连接数据库,而不用繁琐的编写底层代码
0.0.2 数据库连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

0.1 JDBC 是什么?

JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API,换句话说:JDBC是用于在Java语言编程中与数据库连接的API。

0.2 JDBC执行流程
1).连接数据库

2).执行需要的操作

3).关闭数据库资源

0.3 JDBC基础中用到的类或接口
0.3.1 Connection
1).Connection接口位于java.sql包当中,是与数据库连接的对象,只有获得特定的数据库连接对象,才可以访问数据库进行数据库操作。

2).void close() :用于关闭数据库连接

3).Statement createStatement() :返回一个Statement对象

4).PreparedStatement prepareStatement(String sql) :
返回PreparedStatement(预编译的Statement)对象,即将SQL语句提交到数据库进行预编译

5).void setAutoCommit(boolean autoCommit) :关闭自动提交,打开事务

6).void commit() :提交事务

7).void rollback() :回滚事务;

8).void setTransactionIsolation(int level) :设置事务的隔离级别;

0.3.2 DriverManager类
1).该类中包含了与数据库交互操作的方法,该类中的方法全部有数据库厂商提供

2). Connection getConnection(String url,Properties info) :
根据指定数据库连接URL,以及数据库连接属性信息建立数据库连接Connection。url为数据库连接URL,info是数据库连接属性。

0.3.3 Statement接口
1).Statement接口是Java程序执行数据库操作的重要接口,用于已经建立数据库连接的基础之上,向数据库发送要执行的SQL语句。它用于执行不带参数的简单SQL语句。

2).excuteUpdate int excuteUpdate(String sql) :
执行SQL语句中DML类型(insert、update、delete)的SQL语句,返回更新所影响的行数

3).ResultSet excuteQuery(String sql) :
执行查询类型(select)的SQL语句,此方法返回查询所获取的结果集ResultSet对象

4).void addBatch(String sql) :
该方法是将SQL语句添加到此Statement对象的当前命令列表中

5).void clearBatch() :
该方法是清空Statement对象中的命令列表

6).void close() :
该方法是立即释放此Statement对象的数据库和JDBC资源

0.3.4 PreparedStatement接口
1).作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能

2).void setXxx(int parameterIndex,Xxx x) :
将Xxx类型的x作为SQL语句中的参数值,parameterIndex为参数位置索引

0.3.5 Result接口
1).数据库结果集的结果表,通常通过查询数据库的语句生成

2).boolean next() :
将光标位置向后移动一行,如移动的新行有效返回true,否则返回false。

3).Xxx getXxx(String columnLabel) :
以Xxx的方式获取ResultSet对象当前行中指定列的值,参数columnLabel为列名称

1.建立数据库连接操作

1.1 versions1.0
1).创建一个Driver的对象
2).创建Properties对象接收基本信息
3).准备连接数据库的基本信息(user,password,url,driverClass)
4).调用Driver接口的connect(url,info)获取数据库连接

		//1.创建一个Driver实现类的对象
        Driver driver = null;
        try {
            driver = new com.mysql.cj.jdbc.Driver();

	        //2.准备连接数据库的基本信息:url,user,password
	        String url = "jdbc:mysql://localhost:3306/TTMS?characterEncoding=UTF8&serverTimezone=UTC";
	        Properties info = new Properties();
	        info.put("user","root");
	        info.put("password","123456");
	        
	        //3.调用Driver接口的connect(url,info)获取数据库连接
	        Connection  connection = driver.connect(url,info);
	        
        } catch (SQLException e) {
            e.printStackTrace();
        }

1.2 versions1.1
1).读取 jdbc.properties 文件
(jdbc.properties存储着创建数据库需要的基本信息)
2).创建Properties对象接收基本信息(加载对应输入流)
3).通过反射创建Driver对象
4).通过Driver的connect(url,info)获取数据库连接

		String driverClass = null;
        String jdbcUrl = null;
        String user = null;
        String password = null;

        //读取 jdbc.properties 文件
        InputStream in =
                getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        
        //加载对应的输入流
        properties.load(in);
        driverClass = properties.getProperty("driver");
        jdbcUrl = properties.getProperty("jdbcUrl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");

        //通过反射创建 Driver对象
        Driver driver = (Driver) Class.forName(driverClass).newInstance();

        Properties info = new Properties();
        info.put("user",user);
        info.put("password",password);

        //通过 Driver 的 connect 方法获取数据库连接
        Connection connection = driver.connect(jdbcUrl,info);

jdbc.properties文件

driver=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/JWGL?characterEncoding=UTF8&serverTimezone=UTC
user=root
password=123456

1.3 versions1.2 (在不用数据库连接池之前的常用方法)
1).读取 jdbc.properties 文件
(jdbc.properties存储着创建数据库需要的基本信息)
2).创建Properties对象接收基本信息(加载对应输入流)
3).通过反射创建Driver对象
4).加载数据库驱动程序
5).通过DriverManager 的 getConnection() 方法获取数据库连接

	/**
     * Connection 代表应用程序和数据库的一个连接
     */
    public  static Connection getConnection() throws Exception {
        //0.读取 jdbc.properties
        /**
         * 1).属性文件对应 Java 中的 Properties 类
         * 2).可以使用类加载器加载 bin 目录(类路径下)的文件
         */
         
        Properties properties = new Properties();
        InputStream inStream =
                JDBCTool01.class.getClassLoader().getResourceAsStream("jdbc.properties");
        properties.load(inStream);
        
      	/*JDBCTool01为自己写的JDBC基础工具类,即本方法所在的类*/

        //1.准备获取连接的4个字符串:driverClass,jdbcUrl,user,password
        
         String driverClass = properties.getProperty("driver");
         String jdbcUrl = properties.getProperty("jdbcUrl");
         String user = properties.getProperty("user") ;
         String password = properties.getProperty("password");

        //2.加载驱动:Class.forName(driverClass)
        Class.forName(driverClass);

        //3.调用 DriverManager 的 getConnection(jdbcUrl,user,password) 方法获取数据库连接
        Connection connection =
                DriverManager.getConnection(jdbcUrl,user,password);

        return connection;
    }

1.4 versions 2.0 (通过数据库连接池建立数据库连接)
1).初始化数据库连接池
2).从数据库连接池中获取连接
dbcp.properties

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/JWGL?characterEncoding=UTF8&serverTimezone=UTC
username=root
password=123456

dbcp数据库连接池

	 private static DataSource dataSource = null;
    //数据库连接池只初始化一次
    static {

        Properties properties = new Properties();
        InputStream inputStream =
                JDBCTool01.class.getClassLoader().getResourceAsStream("dbcp.properties");
                /*JDBCTool01为自己写的JDBC基础工具类,即本方法所在的类*/
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Connection 代表应用程序和数据库的一个连接
     */
    public  static Connection getConnection() throws Exception {

        return dataSource.getConnection();
    }

2.对数据库进行增删改操作

2.1 versions1.0 (Statement基本不用,只做入门学习)
1).获取数据库连接
2).准备插入的SQL语句
3).执行插入操作
3.1).获取操作DWL语句的Statement对象
3.2).调用Statement的 executeUpdate(sql)执行SQL语句
4).关闭数据库资源

 		//1.获取数据库连接

        testDriver t = new testDriver();		//将获取连接的步骤封装
        Connection connection = null;
        Statement statement = null;
        try {
            connection = t.getConnection();
        //2.准备插入的SQL语句
        String sql ="INSERT INTO `jwgl`.`sc`(`Sno`,`Cno`,`Grade`)VALUES('2','2',1);";
        //3.执行插入
        //1).获取操作SQL语句的Statement对象
        statement = connection.createStatement();

        //2.)调用Statement 的 executeUpdate(sql)执行SQL语句
        statement.executeUpdate(sql);


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

2.2 versions 2.0 (PreparedStatement )
1).获取数据库连接
2).准备插入的SQL语句
3).将SQL语句存入PreparedStatement对象中
4).使用PreparedStatement对象填充占位符
5).调用executeUpdate()执行SQL语句
6).关闭数据库资源

	/**
     * PreparedStatement : 是Statement 的子接口,可以传入带有占位符的SQL语句,
     *                      并且停工了补充占位符变量的方法
     */
    public static void usePreparedStatement(Connection connection, String sql , Object ...args){
        
        PreparedStatement preparedStatement = null;
        try {
            //1.获取PreparedStatement对象
            preparedStatement = connection.prepareStatement(sql);
            for(int i = 0 ; i<args.length;i++){
                preparedStatement.setObject(i+1,args[i]);
            }

            //2.使用executeUpdate 方法 执行 SQL 语句
            preparedStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            releaseDB(connection,preparedStatement,null);
        }
    }

2.3 PreparedStatement与Statement比的优点

	1).PreparedStatement可以写动态参数化的查询
	2).PreparedStatement比 Statement 更快
	3).PreparedStatement可以防止SQL注入式攻击

3.对数据库的查询操作

2.1 versions1.0
1).获取数据库连接(connection)
2).获取PreparedStatement
3).准备SQL语句(将其放入PreparedStatement,并填充其占位符)
4).使用PreparedStatement的executeQuery()方法查询,并创建ResultSet的对象来接收查询结果
5).对查询结果进行想要的操作
6).关闭数据库资源

/*SC为测试的对象类*/
public static SC useResultSet(Connection connection, String sql){
        Statement statement = null;
        ResultSet resultSet = null;
        SC sc = null;
        try {
            //1.获取数据库连接
            //connection = getConnection();

            //2.调用 Connection 对象的 createStatement() 方法获取 Statement 对象
            statement = connection.createStatement();

            //3.准备SQL语句
            /* String sql = "SELECT Sno,Cno,Grade FROM JWGL.SC";*/

            //4.调用 Statement 对象的 executeQuery() 方法,发送 SQL 语句,返回 ResultSet 结果集对象
            resultSet = statement.executeQuery(sql);

            //5.处理结果集
            //1).调用ResultSet 的 next() 方法:查看结果集的下一条记录是否有效,有效则下移指针
            while(resultSet.next()){
                //2).getXxx() 方法获取具体列的值
                sc = new SC();
                sc.setSno(resultSet.getString(1));
                sc.setCno(resultSet.getString(2));
                sc.setGrade(resultSet.getInt(3));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.关闭数据库
            releaseDB(null,statement,resultSet);

            return sc;
        }

2.2 versions 2.0
1).获取数据库连接(connection)
2).获取PreparedStatement
3).准备SQL语句(将其放入PreparedStatement,并填充其占位符)
4).进行查询得到ResultSet
5).利用ResultSet的getMetaData()方法获得ResultSetMetaData对象
6).处理ResultSet结果集
6.1).由ResultSetMetaData得到对象结果集中的列数
6.2).由ResultSetMetaData得到每一列的别名
6.3).由ResultSet得到具体每一列的值
7).创建一个Map,由上面的信息填充Map
8). 利用反射创建class对应的对象
9).遍历Map对象,利用反射为Class对象对应的属性赋值
10).关闭数据库资源

public <T> List<T> get(Class<T> clazz , String sql , Object ...args){
        List<T> list =new ArrayList<>();

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

        try {
            //1.获取Connection
            connection = JDBCTool01.getConnection();

            //2.获取PreparedStatement
            preparedStatement = connection.prepareStatement(sql);
            //3.填充占位符
            for (int i = 0;i<args.length;i++){
                preparedStatement.setObject(i+1,args[i]);
            }

            //4.进行查询得到ResultSet
            resultSet = preparedStatement.executeQuery();

            //5.若ResultSet非空,准备一个List<Map<String,Object>>:键:存放列的别名 值:存放列的值
            //一个Map对应一个记录
            List<Map<String,Object>> values = new ArrayList<>();
            Map<String,Object> map = null;

            //6.得到 ResultSetMetaData 对象
          	//ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            List<String> columnLabels = getColumnLabels(resultSet);
           
            /*用自定义函数取得 columnLabels,则不用再获取ResultSetMetaData*/

            while(resultSet.next()) {
                map = new HashMap<>();

                //7.处理ResultSet结果集

                //8.由ResultSetMetaData
                // 1).得到对象得到结果集中的列数

                for (String columnLabel : columnLabels) {
                    // 2).得到每一列的别名
                   // String columnLabel = resultSetMetaData.getColumnLabel(i + 1);
                    // 3).由ResultSet得到具体每一列的值
                    Object columnValue = resultSet.getObject(columnLabel);

                    //9.填充 Map 对象
                    map.put(columnLabel, columnValue);
                }
                //将得到的Map放入values
                values.add(map);
            }
                //参数对应的Object对象
                T bean =null;

                //10.当List不为空,则遍历每一个Map对象,并转化为Class
                if (values.size()>0){
                    for (Map<String ,Object> m : values){
                        bean = clazz.newInstance();
                        //11.遍历 Map 对象,用反射填充对象的属性值
                        for (Map.Entry<String,Object> entry : m.entrySet()){

                            String propertyName = entry.getKey();
                            Object value = entry.getValue();
                            ReflectionUtils.setField(bean.getClass(),propertyName,bean,value);
                         //   BeanUtils.setProperty(bean,propertyName,value);
                       
                         /*ReflectionUtils为反射工具类,和BeanUtils功能原理相同,利用反射填充对象*/
                        }

                        list.add(bean);
                    }
                }

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCTool01.releaseDB2(connection,preparedStatement,resultSet);

        }
        return list;
    }

    /**
     * 获取结果集的ColumnLabel
     * @param resultSet
     * @return
     * @throws Exception
     */
    private  List<String> getColumnLabels(ResultSet resultSet)throws Exception{
        List<String> labels = new ArrayList<>();

        ResultSetMetaData resultSetMetaData =resultSet.getMetaData();

        for (int i= 0 ; i<resultSetMetaData.getColumnCount();i++){
            labels.add(resultSetMetaData.getColumnLabel(i+1));
        }

        return  labels;
     }

4.数据库的事务处理

4.1 开始事务(取消默认提交)


   
    /**
     * 开始事务
     * @param connection
     */
    public static  void beginTx(Connection connection){
        if(connection != null){
            try {
                connection.setAutoCommit(false);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

4.2 提交事务(如果所有SQL语句都执行完则提交)

/**
     * 提交事务
     * @param connection
     */
    public static void commit(Connection connection){
        if(connection != null){
            try {
                connection.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

4.3 回滚事务(如果出现异常则回滚事务)

 /**
     * 回滚事务
     * @param connection
     */
    public static void rollback(Connection connection){
        if(connection != null){
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

5.批量处理数据库操作

批量处理事务即
1).先用PreparedStatement.addBatch()方法“积攒”SQL语句
2).“积攒”到目标值,统一执行一次PreparedStatement.executeBatch()
3).清空执行过的SQL语句PreparedStatement.clearBatch()

/*部分代码展示*/

			//开始插入数据
            for (int i = 0 ; i<9999;i++){
                preparedStatement.setInt(1,i+1);

                //积攒 SQL
                preparedStatement.addBatch();

                //积攒到指定数量,统一执行一次,并清空积攒的SQL语句
                if((i+1)%300 == 0){  //执行条件

                    preparedStatement.executeBatch();
                    preparedStatement.clearBatch();
                }
            }
            //若总条数不是指定数量的整倍数,则单独执行一次
            if(10000%300 != 0 ){
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }

6.BLOB(二进制大对象)

JDBC处理BLOB
1.增删改操作

String  sql  = "INSERT INTO SC VALUES(?,?,?,?)";
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection =  JDBCTool01.getConnection();
            preparedStatement  = connection.prepareStatement(sql);

            preparedStatement.setString(1,"q");
            preparedStatement.setString(2,"p");
            preparedStatement.setInt(3,123);
            InputStream inputStream = new FileInputStream("p.jpg");
            preparedStatement.setBlob(4,inputStream);

            preparedStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCTool01.releaseDB2(connection,preparedStatement,null);
        }

2.查询操作

 String sql = "SELECT Sno,Cno,Grade,picture FROM SC WHERE Grade = 123 ";
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JDBCTool01.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();

            if(resultSet.next()){
                System.out.println(resultSet.getString(1));

                Blob picture = resultSet.getBlob(4);

                InputStream inputStream = picture.getBinaryStream();
                OutputStream outputStream = new FileOutputStream("img1.jpg");

                byte[] buffer = new byte[1024];
                int len = 0;
                while((len = inputStream.read(buffer)) != -1){
                    outputStream.write(buffer,0,len);
                }

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

7.关闭数据库资源

	 /**
     * 关闭数据库资源
     */
    public  static  void releaseDB(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (preparedStatement != null){
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐