Java--JDBC连接数据库
我们知道Java中的jdbc是用来连接应用程序和数据系统的,本篇文章主要就来看看关于JDBC的实现和使用细节。主要包含以下几点内容:
- JDBC的基本知识(数据驱动程序)
- JDBC的连接配置
- 使用JDBC增删改查
- JDBC的一些使用细节
一、JDBC的基本知识
我们用Java写的程序,无论是桌面应用程序还是web应用程序都是不能直接访问我们本机上的数据库系统的,这就需要使用驱动程序去构成两者之间的连接。
像这样,我们的应用程序需要针对不同的数据库系统调用不同的驱动程序来连接操作数据库系统,但是对于不同的数据库系统,我们就需要学习他们各自提供的驱动程序接口的使用,还是比较麻烦的。并且程序一旦数据迁移,将导致关于数据操作的代码模块需要重写。
于是sun公司为了简化Java对数据库的连接操作,定义了一套Java操作数据库的规范,JDBC(Java Database Connectivity)。从此程序员就可以使用纯Java代码连接和操作数据库了。
JDBC向上提供了一系列的使用接口,包括连接数据库,增删改查操作等。向下会去调用相对应了驱动程序,然后这些驱动程序又会去直接的操作数据库,执行sql语句,返回结果。对于我们程序员,只需要学习怎么使用JDBC,不用再去关心各个驱动程序怎么使用。
二、JDBC的连接配置
想要成功的使用jdbc连接我们的本地数据库主要需要以下几个步骤:
- 下载对应的数据库系统提供的驱动程序
- 将驱动程序包添加到jdk包中
- 调用 DriverManager 类的 getConnection()获取数据库连接对象
下面一步步演示并解释,首先下载对应的DBMS(数据库管理系统提供驱动程序),你可以使用IBM的DB2,或者微软的Sql Server,或者mysql。本篇文章使用的是sql server。下载地址:https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774 。运行sqljdbc_6.0.8112.100_chs.exe解压文件,或者直接解压sqljdbc_6.0.8112.100_chs.tar.gz,然后进入解压出来的文件夹,找到jre文件夹,(应该有两个版本7,8),找到对应自己的jdk版本,复制里面的sqljdbc42.jar包,到本机的jdk文件夹。
默认应该在:C:\Program Files\Java中,进入jdk文件夹中(不要进错了),然后jre\lib\ext,将刚刚的jdbc包粘贴到其中即可。
下面打开IDE创建一个project,main函数中粘贴以下代码,具体什么意思,后文会说。
public static void main(String[] args){
String driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=你的某个数据库的名字";
String userName="你的数据库登录名";
String userPwd="你的数据库登录密码";
try{
//加载驱动程序
Class.forName(driverName);
//获取连接对象
Connection dbConn= DriverManager.getConnection(dbURL, userName, userPwd);
System.out.println("数据库连接成功");
}
catch(Exception e)
{
e.printStackTrace();
}
}
运行以上代码如果打印连接成功,就继续下文。否则,可以评论留言或者自行百度解决。下面开始解释每一条语句:
首先,所有的操作的前提都是告诉jvm我们的程序将要使用的数据驱动是什么,是mysql,sqlserver,还是oracle。Class.forName(driverName);这条语句就是这个作用,我们看driverName字符串。
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");
装载Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");
装载SqlServer驱动:Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
我们上述代码中使用的driverName的值是第三种中的参数值,主流的数据库管理系统是这三者,这三句代码是不变的,读者可以根据自己电脑上的数据库自行选择。
第二步就是获取连接对象,如果没有抛异常就说明连接是成功的,我们首先从三个参数说起。第一个参数是一个URL,他的格式是:jdbc:<子协议>:<子名称>,这种格式基本上也是被每个数据库提供商定死了,你只需要选择他们并增加自己的参数即可。常见的三个URL格式:
对于 Oracle 数据库连接,采用如下形式:
jdbc:oracle:thin:@localhost:1521:sid
对于 SQLServer 数据库连接,采用如下形式:
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid
对于 MYSQL 数据库连接,采用如下形式:
jdbc:mysql://localhost:3306/sid
sid就是本地数据库中某个具体数据库的名字。下面说第二个参数,从命名上读者也是可以轻松的判断出这是在判别身份,第三个参数是密码。相信这三个参数还是可以很轻松理解的。下面看看DriverManager类和他的一些方法。
DriverManager可以叫它驱动程序管理接口,主要实现的是对驱动程序的管理的功能。例如:初始化驱动程序,启动驱动程序建立jdbc连接对象,还有一些获取日志信息的操作。我们主要用它来创建数据库连接对象,然后通过这个对象操作数据库的增删改查。此间如果数据库连接失败将会抛出异常。下面我们介绍这个连接对象,并通过它完成增删改查。
三、使用JDBC增删改查
实现增删改查的操作的前提是需要获取数据库连接对象。以下是这个Connection接口的主要方法:
Statement createStatement()
PreparedStatement prepareStatement(String sql)
CallableStatement prepareCall(String sql)
先看第一个方法,这个方法返回一个Statement对象,这个对象是用来向数据库发送静态sql语句的,第二个方法返回了一个PreparedStatement对象,这是一个预编译的Statement对象,主要用来发送动态的sql语句的。具体的区别下文说。第三个方法返回的对象是用来执行存储过程的相关操作的。
下面我们看可以操作一般sql语句的对象Statement。他有如下常用方法:
ResultSet executeQuery(String sql)
int executeUpdate(String sql)
boolean execute(String sql)
int[] executeBatch()
void addBatch( String sql )
测试方法executeQuery
public static void main(String[] args){
try{
//加载驱动程序
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//获取连接对象
Connection dbConn= DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=FitWeb", "sa", "123456");
Statement statement = dbConn.createStatement();
ResultSet rs = statement.executeQuery("SELECT * FROM users");
while(rs.next()){
System.out.println(rs.getString("name"));
}
rs.close();
dbConn.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//程序的输出结果是我的表FitWeb中users表中字段名为name的所有的值
对于上代码中的ResultSet结果集的相关操作,暂时可以先过。只需要知道我们向executeQuery方法中传入可执行的sql语句,他会返回执行之后的结果。对于方法executeUpdate主要是完成对数据表的增删改。
int x = statement.executeUpdate("UPDATE users SET NAME = '华为' WHERE NAME ='李斯'");
可以像这样修改表中数据,还可以insert,delete等操作。对于execute方法,它不区分是查询还是修改操作,你可以向他传入任意的sql语句,只是对于查询不会返回结果集,如果成功的修改了表中内容返回true,否则false。下面看看批处理sql操作:
Statement statement = dbConn.createStatement();
statement.addBatch("UPDATE users SET NAME ='Walker' WHERE NAME = '华为'");
statement.addBatch("UPDATE users SET NAME ='华为' WHERE NAME = 'Walker'");
int[] count = statement.executeBatch();
for(int c : count){
System.out.print(c);
System.out.print(",");
}
statement.close();
dbConn.close();
//输出结果:1,1
之前的方法都只能一次操作一条sql语句,而我们的executeBatch是用来一次执行多条sql语句的,返回值是int数组,它表示了每一次操作数据库之后影响的行数。第一次sql执行之后影响的行数保存在索引为0中,后面以此类推。关于预编译Statement的操作,我们放在下一个小节说。
四、JDBC的一些使用细节
SQL注入的大名想必大家都是知道的,而在我们之前介绍的方法中,好像都没有关于如何防止这种黑客行为。SQL注入就是指在带有参数的sql语句中注入的sql语法。类似这样:
select * from users where name = userName
如果我们的应用程序中需要查询某个人的信息,而查询的条件是需要用户输入自己的用户名,然后我们根据用户名进行查询,向上面一样如果用户输入的是以下内容:
userName = "'张三' or '李四'";
select * from users where name = userName
等价:
select * from users where name = '张三' or '李四'
这样岂不是将张三和李四的信息都查询出来了,如果此人通过大数据列举,很可能数据表中的所有数据都会被查询出来。
我们可以使用,预编译Statement对象来避免这件事情。先看代码:
PreparedStatement statement = dbConn.prepareStatement("insert into user(id,Name) values (?,?)");
statement.setInt(1,1);
statement.setString(2,"walker");
statement.executeUpdate();
statement.close();
dbConn.close();
程序运行的结果是,插入一条数据到了我的user表中。接下来我们看看每条语句是什么含义。首先,?表示占位符的意思,就是说此处会有参数传入,只是具体的值是什么暂时不知道。下面的两条setXXX就是在为占位符赋值,然后执行更改操作,完成数据库更新。我们说为什么它能够防止Sql注入呢?因为所有用户的输入参数都是用?占位,也就是说无论你传入的是什么,我都只把你当做参数。
userName = "'张三' or '李四'";
select * from users where name = ?
等价:
select * from users where name = ''张三' or '李四''
//相当于:'?',无论?是什么都不会产生任何问题
另外,使用这种预编译Statement,可以很大程度上提高性能,因为他会缓存sql模板,就是除了参数部分,其余内容会被缓存,等到下次再遇到的时候会直接调用,提高性能。因此建议使用这种方式来操作数据库。
最后详细的介绍一下,关于结果集的操作。本篇刚开始的时候用过,但是那只是很简单的一部分。ResultSet(结果集)接口的主要方法如下:
boolean next()
String getString(int columnIndex)
boolean getBoolean(int columnIndex)
int getInt(int columnIndex)
String getString(String columnLabel)
java.sql.Date getDate(int columnIndex)
java.sql.Time getTime(int columnIndex)
java.sql.Timestamp getTimestamp(int columnIndex)
void beforeFirst()
void afterLast()
Blob getBlob(int columnIndex)
Clob getClob(int columnIndex)
这个接口有着四千多行的代码,我们只列举了常用的方法。我们可以将整个结果集理解为一张二维的表,每张表都有一个游标用于遍历所有的行。next()方法用于判断是否还有下一行,返回值是boolean。getXXX方法表示获取当前游标指向的行中指定的字段,可以使用索引来定位字段,也可以是通过字段的名字来定位。如果是索引,1为起始位置。
关于LOB类型数据,这是一类大对象,往往是图片或者是一些其他的数据。二进制大对象称作BLOB,字符型大对象称作CLOB。调用getBlob或者getClob可以获取到相应的对象。通过他们自己内部的getBytes,或者getInputStream等方法,可以获取其中的内容。此处不细说。
下面看看多结果集的操作,看代码:
Statement statement = dbConn.createStatement();
boolean b = statement.execute("select * from users;select * from sports;");
if(b){
ResultSet rs = statement.getResultSet();
while (rs.next()){
System.out.println(rs.getString("name"));
}
b = statement.getMoreResults();
if (b){
ResultSet rs2 = statement.getResultSet();
while (rs2.next()){
System.out.println(rs2.getString("sportsName"));
}
}
}
statement.close();
dbConn.close();
我们调用execute方法执行sql语句,而需要执行的是两条sql语句,如果成功的获取了结果集则返回true。通过getResultSet方法获取第一个结果集,输出所有字段名为name的信息,调用getMoreResults方法判断是否有别的结果集,如果有返回true。然后通过getResultSet获取当前结果集,也就是第二个结果集,输出信息。
为了不使文章篇幅过长,还剩下一个知识点放在下篇。