Jdbc连接数据库(2)

一、不足之处

上一节讲到,jdbc连接数据库的操作是:
1、导入jar包;
2、反射加载驱动;
3、创建连接 ;
4、创建sql语句;
5、生成Statement对象;
6、执行sql语句;
7、获取结果,遍历ResultSet;
8、关闭连接。
其部分执行代码是:

	 //反射加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接
            connection = DriverManager.getConnection(url, username, password);
            String sql="select * from user where id> ? ";
            //生成PreparedStatement,可以防止sql注入
            prestmt=connection.prepareStatement(sql);
            //设置参数
            prestmt.setString(1,"0");   //设置id条件
            resultSet = prestmt.executeQuery();             //返回执行结果
            if(resultSet!=null){
                while (resultSet.next()){
                    //resultSet.next(): 指向返回结果的下一行数据
                    System.out.println(resultSet.getString(1)+resultSet.getString(2));  //通过columnIndex获取每一行的每一列数据
                }
            }

对于开发过程中,1、Jdbc操作数据库的时候如果每一个方法都这么繁琐的写,代码是很重复的、冗余的;2、代码中存在硬编码问题,对于url、username、password、driver这些常用变量,我们是写死在代码中,如果有变量发生变化,那我们还需要修改代码,很不友好。
解决措施:
1、我们可以建一个工具类JdbcUtil来封装JDBC数据库操作的代码,用到的时候调用工具的方法就可以。
2、将url、username、password、url写到一个文件jdbc.properties文件下,避免硬编码。

二、JdbcUtil工具类

1、jdbc.properties文件与反射加载驱动

// jdbc.properties的内容是:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/testPro?useSSL=false

jdbc连接数据库的操作第一步 是:1、反射加载驱动。所以,我们需要在第一步之前就获取到jdbc.properties文件的内容(而且仅加载一次)。在JdbcUtil工具类中,我们可以在static{}静态代码块里读取配置文件的内容,具体如下。

///静态代码块读取配置文件
public class JdbcUtil{
    //1、创建properties集合类
    Properties pro=new Properties();
    /**static静态代码块属于类,会随着类的创建而创建,在方法之前,且仅执行一次*/
    static{
        try{
             //2、类加载器加载文件
            //jdbc.properties的文件在src或则resources目录下
            InputStream resourceAsStream = JdbcUtil3.class.getClassLoader().getResourceAsStream("jdbc.properties");
            //3、pro加载输入流
             pro.load(resourceAsStream);
            //4、可以通过pro.getProperty(String key)获取对应的值
            String driver=pro.getProperty("jdbc.driver");
            //5、反射加载驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
       
    }
}

2、创建连接

在JdbcUtil工具类中创建获取连接的静态方法,可以通过:类名.方法名进行调用。

可以通过JdbcUtil.getConnection();方法获取数据库连接;(前提是读取jdbc.properties里面的信息)

    public static Connection getConnection(){
        String username=pro.getProperty("jdbc.username");
        String password=pro.getProperty("jdbc.password");
        String url=pro.getProperty("jdbc.url");
        Connection connection=null;
        try {
            //获取连接
             connection=DriverManager.getConnection(url,username,password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

3、创建Statement

	//创建Statement不需要单独写在JdbcUtil方法里面
 	Statement statement=null;
        String sql="select * from user where username=? and password=? ";
        try {
            //connection通过JdbcUtil.getConnection()获取
            statement=connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }

4、执行sql

在JdbcUtil类中,执行sql有两种方法(查询和更新):executeQuery()和executeUpdate()

	  /***
     * 查询操作
     * @param prestmt  prestmt 包含sql的PreparedStatement
     * @param params  Object...params ,可变参,相当于数组,因为长度不确定,放在方法参数最后
     * @return 查询结果的List<Map>格式
     */
	public static List<Map> executeQuery(PreparedStatement prestmt, Object...params){
        List<Map> result=new ArrayList<>();
        ResultSet set=null;
        try {
            /**给查询的sql设置参数,替换sql中的 ? */
            for(int i=1;i<=params.length;i++){
                prestmt.setObject(i,params[i-1]);
            }
            set=prestmt.executeQuery();
            //获取查询结果的虚拟表的结构(列的类型,列的个数,列字段名)
            ResultSetMetaData metaData=set.getMetaData();
            //获取查询结果的虚拟表的列数
            int countColumn=metaData.getColumnCount();
            if(set!=null){
                //通过set.next() 遍历每一条查询结果
                while (set.next()){
                    Map map =new HashMap();
                    //遍历每一行结果的 每一列,将每一条结果封装在map中
                    for(int i=1;i<=countColumn;i++){
                        //获取  {key:value}
                        map.put(metaData.getColumnLabel(i),set.getObject(i));
                    }
                    result.add(map);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

   /***
     * 更新操作
     * @param prestmt 包含sql的PreparedStatement
     * @param params  Object...params ,可变参,相当于数组
     * @return sql更新影响的条数
     */
    public static int executeUpdate(PreparedStatement prestmt,Object...params){
        int result=0;
        try {
            for(int i=1;i<=params.length;i++){
                prestmt.setObject(i,params[i-1]);
            }
            //更新操作
            result=prestmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

5、关闭连接

对于Connection、Statement、ResultSet的对象,在使用完之后是必须关闭的。遵循的原则是:先开启后关闭,后开启先关闭。其对象新建顺序是:Connection、Statement、ResultSet,所以关闭顺序是:ResultSet、Statement、Connection 对象。

  /**
     * 关闭所有连接
     * @param connection
     * @param statement
     * @param set
     */
    public static void closeAll(Connection connection, Statement statement, ResultSet set){
        try {
            if(set!=null){
                set.close();
            }
            if(statement!=null){
                statement.close();
            }
            if(connection!=null){
                connection.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

二、注意事项

(1)jdbc.properties配置文件的位置应该是src目录下, 或则resources目录下。

/// 注意:jdbc的格式, = 左右两侧没有 "",
//eg:"jdbc.driver"="com.mysql.cj.jdbc.Driver"是不合适的 
jdbc.driver=com.mysql.cj.jdbc.Driver

(2)jdbc.properties文件中 = 的 两侧一般没有双引号

//jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
//错误的取值,取到是null
String driver = pro.getProperty("driver");
//正确的取值
String driver = pro.getProperty("jdbc.driver");

(3)ExceptionInInitializerError
静态初始化程序中发生意外异常的信号。抛出 ExceptionInInitializerError
表明在计算静态初始值或静态变量的初始值期间发生异常。
在JdbcUtil工具类中,static{}静态代码块可能出现错误
(4)PreparedStatement的对象 prestmt可以使用占位符 ? ,设置参数的时候是从 1 开始,而数组索引是从0 开始

      /**给查询的sql设置参数,替换sql中的 ? */
            for(int i=1;i<=params.length;i++){
                prestmt.setObject(i,params[i-1]);
            }

(5)Statement与PrepaedStatement对象
关系:PrepaedStatement继承Statement,都是接口
区别:Statement执行静态sql,不能够设置参数
​ PreparedStatement执行动态sql,可以设置参数。而且基于预编译sql,可以防止sql注入。

注:sql预编译存储在PreparedStatement对象中。

///1、Statement执行静态sql
   String sql="select * from user where username= 'admin' and password= 'admin' ";
        Statement statement = JdbcUtil.getConnection().createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()){
            System.out.println(resultSet.getString(2));
        }
///2、PrepaedStatement执行动态sql
  String sql="select * from user where username= ? and password= ?";
        PreparedStatement preparedStatement=JdbcUtil3.getConnection().prepareStatement(sql);
        preparedStatement.setString(1,"admin");
        preparedStatement.setString(2,"admin");
        ResultSet resultSet = preparedStatement.executeQuery();
        while(resultSet.next()){
            System.out.println(resultSet.getString(2));
        }

(7)java.sql.SQLException: Before start of result set
出现问题的代码:

 		String sql="select * from user where username= 'admin' and password= 'admin' ";
        Statement statement = JdbcUtil.getConnection().createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
		
		System.out.println(resultSet.getString(2));

出现的原因:

符合条件的结果只有一条,忽略了循环。

ResultSet的结果的初始游标位置是 查询到结果数组的前一位,忽略next(),此时去取值就会出现: Before start of result set

resultSet.next()方法可以使得游标每次下移一行,只有下移一行才能找到数据。
修改:

	while(resultSet.next()){
		System.out.println(resultSet.getString(2));
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值