java中的JDBC详解 附带实现配置文件访问数据库

JDBC 是Java操作数据库的规范,它实际上定义了一组标准的数据库的接口,为了实现通过java操作数据库,必须实现这些接口,不同的数据库厂商都提供了对JDBC接口的实现,这些具体的实现被打包成一个jar包(也就是数据库驱动),供我们在开发的时候直接使用。
JDBC API 中的主要接口:
第一: Driver接口是所有JDBC程序必须实现的接口,该接口专门提供给数据库厂商使用,定义了驱动的样式
第二:DriverManager 用于加载JDBC驱动并创建与数据库的连接
有两个重要的方法:
1 DriverManager.registerDriver(Driver driver) // 用于向DriverManager注册给定的JDBC驱动程序
2 DriverManager.getConnection(String url, String user, String pwd) // 建立与数据库的连接,返回表示连接的Connection对象
第三: Connection 接口 主要方法有三个
1. Connection.createStatement(); // 创建一个Statement对象,静态sql语句查询
2. Connection.prepareStatement(String sql); // 创建一个PreparedStatement对象,实现动态sql语句查询
3. Connection.prepareCall(String sql); // 创建一个CallableStatement对象来调用数据库存储过程
第四:Statement接口 用于执行查询返回查询结果
1 Statement.execute(String sql); // 执行各种SQL语句,返回一个boolean类型值,true表示执行的SQL语句具备查询结果,可通过Statement.getResultSet()方法获取
2 Statement.executeUpdate(String sql); // 执行SQL中的insert/update/delete语句,返回一个int值,表示受影响的记录的数目
3 Statement.executeQuery(String sql); // 执行SQL中的select语句,返回一个表示查询结果的ResultSet对象
第五:ResultSet接口 用于查询结果的操作
1 ResultSet.next(); // 将游标由当前位置移动到下一行
2 ResultSet.getString(String columnName); // 获取指定字段的String类型值
3 ResultSet.getString(int columnIndex); // 获取指定索引的String类型值
4 ResuleSet.previous(); // 将游标由当前位置移动到上一行

JDBC操作数据库的一般步骤
注册驱动 (只做一次)
建立连接(Connection)
创建执行SQL的语句(Statement)
执行语句并处理执行结果(ResultSet)
释放资源

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

// 这里测试的是mysql数据库名是test; 表是 user; 表的字段 有 id ,name ,age ,salary
public class JDBCTest {
    public static void main(String[] args) {
        // 第一步: 首先注册驱动, 驱动一般只会注册一次
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            System.out.println("找不到驱动程序类,加载驱动失败");
            e.printStackTrace();
        }
        // 第二步:建立连接 Connect, 设置url ,用户名, 密码
        // url格式:JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&…
        // 注意的是url中一定不要加多余的空格,否则会出错, useSSL=false是为了解决身份验证时出现的警告的问题
        // String url = "jdbc:mysql://localhost:3306/test?" + "user=root&password=wsw011152&useUnicode=true&characterEncoding=UTF-8&useSSL=false";
        String url = "jdbc:mysql://localhost:3306/test?useSSL=false";
        String name = "root";
        String psw = "root";
        Connection connect = null;
        try {
            connect = DriverManager.getConnection(url, name, psw);
            // connect = DriverManager.getConnection(url);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            System.out.println("数据库连接失败");
            e.printStackTrace();
        }
        // 第三步: 创建一个 Statement ,一般建议使用 PreparedStatement
        // 1、执行静态SQL语句。通常通过Statement实例实现。
        // 2、执行动态SQL语句。通常通过PreparedStatement实例实现。
        // 3、执行数据库存储过程。通常通过CallableStatement实例实现。
        // String sql = "select * from user where id = ?";
        String sql = "select * from user where id = ?";
        try {
            PreparedStatement ps = connect.prepareStatement(sql);
            ps.setInt(1, 1); // 设置参数
            // 第四步: 执行语句,获得一个结果集,处理获得的结果
            ResultSet result = ps.executeQuery();
            while (result.next()) {
                System.out.println(result.getInt("id"));
                System.out.println(result.getString("name"));
                System.out.println(result.getInt("age"));
                System.out.println(result.getString("salary"));
            }
            // 第五步: 关闭资源
            result.close();
            ps.close();
            connect.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

下面讲解一下 JDBC为什么建议使用PreparedStatement而不是Statement
首先java提供了三种方式来执行sql语句
CallableStatement;主要用于存储过程的查询,
Statement;主要用于通用查询,适合只对数据库进行一次性存取的时候,使用它会为每一条sql语句生成一个执行计划,即使这两条语句只有参数的不同而已。
PreparedStatement :主要用于参数化查询,会传递参数,反复查询

PreparedStatement 相对比与Statement的优势在下面的几点:

1. 使用PreparedStatement,数据库系统会对sql语句进行预编译(需要JDBC驱动支持预编译SQL查询),进行预处理,这条预处理的sql查询语句可以在将来的查询中被重用,节省了创建执行计划的时间,减少了系统的开销,因此它比Statement的查询速度更快。
比如使用 Statement进行下面两句的查询,则会生成两个执行计划,1000个查询就会生成1000个执行计划,生成执行计划十分消耗资源,
select colume from table where colume=1;
select colume from table where colume=2;
但是使用PreparedStatement, 则系统会对sql语句进行预编译处理,只会生成1个执行计划,1000个这样的查询不会再产生执行计划,这个执行计划会被下面同样的查询语句所重用,大大提高了速度。
select colume from table where colume=?;
PreparedStatement .setInt(1, 1);
PreparedStatement .setInt(1, 2);
2. 可以写动态参数化的查询,用PreparedStatement你可以写带参数的sql查询语句,通过使用相同的sql语句和不同的参数值来做查询

3. PreparedStatement可以防止SQL注入式攻击,更加安全
使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入攻击。
第一:在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行
第二:在组合SQL字符串的时候,先对所传入的参数做字符取代(将单引号字符取代为连续2个单引号字符,因为连续2个单引号字符在SQL数据库中会视为字符中的一个单引号字符

strSQL = “SELECT * FROM users WHERE name = ‘” + userName + “’;”
传入字符串:
userName = ” 1’ OR 1=1 “

把userName做字符替换后变成:
userName = ” 1” OR 1=1”

最后生成的SQL查询语句为:
strSQL = “SELECT * FROM users WHERE name = ‘1” OR 1=1’
这样数据库就会去系统查找name为“1′ ‘ OR 1=1”的记录,而避免了SQL注入。

PreparedStatement的局限性:
为了防止SQL注入攻击,PreparedStatement不允许一个占位符(?)有多个值,在执行有*IN子句查询的时候这个问题变得棘手起来*。下面这个SQL查询使用PreparedStatement就不会返回任何结果:
SELECT * FROM loan WHERE loan_type IN (?)
preparedSatement.setString(1, “‘personal loan’, ‘home loan’, ‘gold loan’”);
解决方式:

// 将in 里面的变量首先存储成一个数组
String[] in_datas=new String[]{"1", "2", "3"}; 
StringBuffer buffer = new StringBuffer();
for(int i=0;i<in_datas.length-1;i++){ 
    buffer.append("?,"); 
}
buffer.append("?");
// 在in字句 里面使用N个 ?。 然后为每一个?赋值
pst = conn.prepareStatement("select id,name from B where id in ( "+buffer.toString()+" )");  //  buffer.toString() ="?,?,?,?...?"
for(int i=0;i<in_datas.length;i++){  
    pst.set(i, in_datas[i]);  
}
// 解决 like 查询的方式
   String expr = "select * from  table where url like ?";  
   pstmt = con.prepareStatement(expr);  
   String a="a";  
   pstmt.setString(1, "%"+a+"%");//自动添加单引号 (包装后的参数)  
   pstmt.execute();  

SQL注入:
在SQL注入攻击里,恶意用户通过SQL元数据绑定输入,比如:某个网站的登录验证SQL查询代码为:
strSQL = “SELECT * FROM users WHERE name = ‘” + userName + “’ and pw = ‘”+ passWord +”’;”
恶意填入:
userName = “1’ OR ‘1’=’1”;
passWord = “1’ OR ‘1’=’1”;
那么最终SQL语句变成了:
strSQL = “SELECT * FROM users WHERE name = ‘1’ OR ‘1’=’1’ and pw = ‘1’ OR ‘1’=’1’;”
因为WHERE条件恒为真,这就相当于执行:
strSQL = “SELECT * FROM users;”
因此可以达到无账号密码亦可登录网站。如果恶意用户要是更坏一点,用户填入:
strSQL = “SELECT * FROM users;”
SQL语句变成了:
strSQL = “SELECT * FROM users WHERE name = ‘any_value’ and pw = ”; DROP TABLE users”
这样一来,虽然没有登录,但是数据表都被删除了。

防止SQL注入的几种方式:
第一种:使用预编译语句,通过绑定变量的方式使得SQL语句的语义不会发生变化,避免使用动态拼接的sql语句进行查询’,传进来的参数只会被当成?代表的变量来使用,这样就无法改变SQL语句的结构。
具体的实现包括: 使用存储过程 或者 使用PreparedStatement实现执行参数化的查询。
第二种: 永远不要相信用户的输入,在web页面就进行传入数据的检验,使用正则表达式等方式过滤特殊的符号,进行校验, 但是一旦出现了新的SQL攻击方式,正则表达式库也需要随之修改
第三种: 做好数据库帐号权限管理,对于用户,尽量不要赋予其管理员的权限进行登录访问。
第四种: 严格加密处理用户的机密信息

下面我自己实现了一个读取数据库配置文件进行数据库的连接,可以连接不同的数据库的一个实现。

第一步:配置文件 database.properties ,存放了各种数据库的配置信息

# mysql database driver
MySQLdriverClass=com.mysql.jdbc.Driver
MySQLurl=jdbc:mysql://127.0.0.1/test?useSSL=false
MySQLusername=root
MySQLpassword=wsw011152
# sqlserver 2008 database driver
SqlServerdriverClass=net.sourceforge.jtds.jdbc.Driver
SqlServerurl=jdbc:jtds:sqlserver://localhost:1433;DatabaseName=test;
SqlServerusername=sa
SqlServerpassword=sa
# Oracle  database driver
OracledriverClass=oracle.jdbc.driver.OracleDriver
Oracleurl=jdbc:oracle:thin:@localhost:1521:test
Oracleusername=scott
Oraclerpassword=tiger

第二步: 创建一个工具类用来读取这个配置文件中的信息

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class PropertiesUtil {
   private static Properties prop;
   static{
        try {
            prop = new Properties();
            prop.load(new FileInputStream("database.properties"));
        } catch (FileNotFoundException e) {
            System.out.println("加载配置文件失败");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 根据数据库名获取其驱动
    public static String getDriverProperties(String key){
        return prop.getProperty(key + "driverClass");
    }
    // 根据数据库名获取url
    public static String getUrlProperties(String key){
        return prop.getProperty(key + "url");
    }
    // 获取用户名
    public static String getUsernameProperties(String key){
        return prop.getProperty(key + "username");
    }
    // 获取密码
    public static String getPasswordProperties(String key){
        return prop.getProperty(key + "password");
    }
}

第三步:连接数据库

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.mysql.jdbc.PreparedStatement;

public class JDBCByProperties {
    public static void main(String[] args) {

        // 只需要将不同的驱动放在配置文件中,就可以实现连接不同的数据库
        try {
            // 1.注册驱动   
            Class.forName(PropertiesUtil.getDriverProperties("MySQL"));
            // 2. 连接数据库
            Connection conn = DriverManager.getConnection(
                    PropertiesUtil.getUrlProperties("MySQL"),       PropertiesUtil.getUsernameProperties("MySQL"),              PropertiesUtil.getPasswordProperties("MySQL"));
            // 3. 创建执行的sql语句, 建议使用PreparedStatement
            String sql = "select * from user where id = ?";
            PreparedStatement prep = null;
            prep = (PreparedStatement) conn.prepareStatement(sql);
            prep.setInt(1, 1);
            // 4. 查询sql 语句, 返回一个结果集
            ResultSet result = prep.executeQuery();
            // 5. 处理结果集, 释放资源
            while (result.next()) {
                System.out.println(result.getInt("id"));
                System.out.println(result.getString("name"));
                System.out.println(result.getInt("age"));
                System.out.println(result.getString("salary"));
            }
            conn.close();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            System.out.println("驱动注册未成功");
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

本文参考了博客:
http://www.importnew.com/5006.html

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java,您可以使用JDBCJava Database Connectivity)来访问数据库。以下是使用JDBC访问数据库的一般步骤: 1. 导入JDBC相关的类和包。您需要导入Java提供的JDBC类和相关的数据库驱动程序包。例如,对于MySQL数据库,您可以使用`com.mysql.jdbc.Driver`类和`mysql-connector-java`驱动程序。 2. 加载数据库驱动程序。使用`Class.forName()`方法加载数据库驱动程序。例如,对于MySQL数据库,可以使用以下代码加载驱动程序: ``` Class.forName("com.mysql.jdbc.Driver"); ``` 3. 创建数据库连接。使用`DriverManager.getConnection()`方法创建一个数据库连接对象,并指定数据库的URL、用户名和密码。例如,对于MySQL数据库,可以使用以下代码创建连接: ``` String url = "jdbc:mysql://localhost:3306/mydatabase"; String username = "root"; String password = "password"; Connection connection = DriverManager.getConnection(url, username, password); ``` 4. 创建Statement对象。使用连接对象的`createStatement()`方法创建一个Statement对象,用于执行SQL语句。 ``` Statement statement = connection.createStatement(); ``` 5. 执行SQL查询或操作。使用Statement对象的`executeQuery()`方法执行SELECT语句,并使用`executeUpdate()`方法执行INSERT、UPDATE、DELETE等更改操作。例如,执行SELECT语句并处理结果可以使用以下代码: ``` String sql = "SELECT * FROM mytable"; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { // 处理每一行数据 } ``` 6. 关闭连接和资源。在完成数据库操作后,记得关闭连接和释放资源,以免造成资源泄漏。可以使用`resultSet.close()`, `statement.close()`和`connection.close()`方法来关闭相关的对象。 这只是一个简单的示例,您可以根据您使用的数据库和具体需求来编写更复杂的代码。希望这些步骤能对您有所帮助!如果您有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值