(六) Java数据库
一、概述
程序开发没有数据库的参与,可以说几乎是不可能的。数据库和Java都已经有了简单的了解,现在的关键是对两者进行连接,起到这一作用的正是JDBC——Java Database Connectivity,数据库桥接。JDBC提供了一整套API使应用程序对数据库进行操作,如下图所示。
首先你要有一个能用的数据库,我比较熟悉MySQL,因此我并不想跳出舒适圈子,所有的数据库操作应该没什么不同,因此就以MySQL为例,MySQL的详细安装过程见这篇随笔。但是只有命令行对数据库的管理造成困难,因此介绍一款数据库图形化界面管理工具Navicat for MySQL(一个系列的,还有针对SQL Server,Oracle和MongoDB的版本)。官网在这https://www.navicat.com.cn/,这样管理起来相当方便。
准备工作的最后一步是下载JDBC驱动jar包(不要去搞什么jdbc-odbc设置),这个非常好找,下载之后解压找到jar包(我的这版名称是mysql-connector-java-5.1.40-bin.jar)导入目标工程中的library中即可,不论你是Eclipse还是IDEA,大致流程都相同,之前对Eclipse已经轻车熟路了,现在展示以下IDEA导入jar到library库中的详细步骤。
第一步:File——Project Structure
第二步:选择library,点击加号添加
第三步:选择Java,找到那个jar包
完结撒花!!等等,这好像才刚刚开始。
整个全部流程如下:
二、数据库连接和SQL“更新”语句的执行
这里我先上一段代码,作用是在数据库中创建一个表。
import java.sql.*; public class Connect { public static void main (String [] args) { String url = "jdbc:mysql://localhost:3306/manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false"; String user = "root"; String password = "123456"; try { // 注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 获得连接对象 Connection connection = DriverManager.getConnection(url, user, password); Statement s = connection.createStatement(); String sql = "create table course " + "( Course_id int primary key," + " CourseNumber varchar(10)," + " CourseName varchar(50))"; s.executeUpdate(sql); s.close(); connection.close(); } catch (SQLException e) { } } }
// 注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 获得连接对象 Connection connection = DriverManager.getConnection(url, user, password);
这两句代码就是通过驱动连接数据库了,首先要注册驱动,当然除了registerDriver方法,还有其他的注册方法,例如Class.forName(“com.mysql.jdbc.Driver”)等,注册驱动之后需要调用getConnection方法返回一个Connection对象,参数是String形式的数据库的url,用户名和密码,即预先设置的String串,其中红色部分代表数据库名称,要进行相应的改变。 返回的Connection对象的最重要的作用应该就是创建另外一个对象,
Statement s = connection.createStatement();
就是Statement对象,Statement对象能够执行SQL语句,其重要性不言而喻,也是任何Java数据库初学者必须要接触的类。执行SQL语句并非只有一个方法,而是分为executeUpdate()和executeQuery()两种,分别执行更新和查询功能。本节介绍更新语句的执行,包括CREATE、DROP、INSERT等不返回结果的SQL语句都被归类于此。
String sql = "create table course " +
"( Course_id int primary key," +
" CourseNumber varchar(10)," +
" CourseName varchar(50))";
s.executeUpdate(sql);
方法调用形式也比较简易,只需将SQL语句的String形式作为参数即可,但是要注意SQL语句不能语法出错,否则异常可不好定位。上述语句将会创建一个名为course的表,并且拥有三个属性。
s.close();
connection.close();
做大事者需要拘小节,不要忘记关闭对象,也许这不会影响你程序的运行,也许会,但是一个良好的编码习惯是必要的。
创建的表,在navicat中的详情展示。
大多数情况下,更新语句的功能都可以可视化的操作,因此这一部分好像显得不是非常重要,但这绝对是要必须掌握的,仅仅只会查询语句和可视化的操作效率未必高、流程未必规范。
三、SQL“查询”语句的执行
本届介绍查询语句的执行,内容比较多,主要在于对查询结果的信息获取和处理。分为对列的处理和对行的处理,先来看对列的处理:
import java.sql.*; public class Select { public String url; public String user; public String password; Select(String a,String b,String c) { url = a; user = b; password = c; } public Connection getConnection () throws SQLException { DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 获得连接对象 Connection connection = DriverManager.getConnection(url, user, password); return connection; } public static void main(String []args) { String url = "jdbc:mysql://localhost:3306/manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false"; String user = "root"; String password = "123456"; try { Select select = new Select(url, user, password); Connection c = select.getConnection(); Statement s = c.createStatement(); String sql = "select * from course"; ResultSet rs = s.executeQuery(sql); ResultSetMetaData rm = rs.getMetaData(); int n = rm.getColumnCount(); for (int i = 0; i < n; i++) { System.out.println("第" + (i + 1) + "列"); System.out.println("名称是:" + rm.getColumnName(i + 1)); System.out.println("类型是:" + rm.getColumnTypeName(i + 1)); System.out.println("精度是:" + rm.getPrecision(i + 1)); } s.close(); c.close(); } catch (SQLException e) { } } }
执行语句和更新基本相同,但是方法换成了executeQuery()方法,然后返回ResultSet类的实例对象,查询结果的所有信息都将从中获得。如想获得查询结果的列信息,还需要调用getMetaData()返回ResultSetMetaData对象。
上述代码的运行结果是:
第1列 名称是:Course_id 类型是:INT 精度是:11 第2列 名称是:CourseNumber 类型是:VARCHAR 精度是:10 第3列 名称是:CourseName 类型是:VARCHAR 精度是:50
处理行可能会是更为关注的内容,也即对查询的结果进行处理。行的处理一般是直接对ResultSet对象进行处理,先看实例
import java.sql.*; public class Select2 { public static void main(String []args) { String url = "jdbc:mysql://localhost:3306/manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false"; String user = "root"; String password = "123456"; try { Select select = new Select(url, user, password); Connection c = select.getConnection(); Statement s = c.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); String sql = "select * from course"; ResultSet rs = s.executeQuery(sql); /* row */ rs.last(); System.out.println("共查询到"+rs.getRow()+"条记录"); rs.beforeFirst(); while(rs.next()) { System.out.println("第"+rs.getRow()+"行:"); System.out.println("id is "+rs.getInt(1)); System.out.println("number is "+rs.getInt(2)); System.out.println("name is "+rs.getString("CourseName")); } s.close(); c.close(); } catch (SQLException e) { } } }
结果如下:
共查询到3条记录 第1行: id is 1 number is 1010 name is Java 第2行: id is 2 number is 1111 name is C 第3行: id is 3 number is 1234 name is C++
数据库中的数据:
首先的与之前的区别在于createStatement()的参数不能再是空的了,多了两个参数,第一个是resultSetType设置返回结果集访问记录的顺序:
第二个参数是resultSetConcurrency设置是否能对结果集进行更新
而程序中的两个参数使得可以对结果集的记录以任意的顺序进行访问,较为常用。
其余的方法可以将ResultSet考虑成一个游标或者指针,last()将其指向最后一条记录,first()则是第一条,beforeFirst()指向第一条之前,next()指向下一条,初始情况指向第一条之前,与beforeFirst()一致,getRow()能够获取当前的行号。而常用的遍历结果集的方法正如代码中所示结合next()方法和getInt()或者getString()这些方法【参数可以是列名,也可以是列标号】。
此外,ResultSet也有打开和关闭两种状态,每个Statement实例对象可以进行多次查询,并产生多个ResultSet对象,但是每个时刻只能有一个处于打开状态,即当每次运行SQL产生新的ResultSet对象时,其他的就会关闭。当Statement对象被关闭时,其产生的ResultSet对象也都会关闭。
插一个JDBC(数据库)和JAVA类型的对照表:
四、带参数的SQL语句执行
实际上带参数的SQL语句是十分常用的,尤其是在嵌入到程序中时。比如当我输入课程号,输出相应的课程名称:
Select select = new Select(url, user, password); Connection c = select.getConnection(); Statement s = c.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); Scanner sc = new Scanner(System.in); int ii = sc.nextInt(); String sql = "select CourseName from course where Course_id = ' " + ii +" '"; ResultSet rs = s.executeQuery(sql); rs.next(); System.out.println("name is " + rs.getString("CourseName")); s.close(); c.close();
结果:
借此实现一些简单的数据库交互操作,但是更常见的方法是使用PreparedStatement,如下
Select select = new Select(url, user, password); Connection c = select.getConnection(); Scanner sc = new Scanner(System.in); int ii = sc.nextInt(); /* PreparedStatement */ PreparedStatement ps = c.prepareStatement("select CourseName from course where Course_id = ?"); ps.setInt(1,ii); ResultSet rs= ps.executeQuery(); rs.next(); System.out.println("name is " + rs.getString("CourseName")); ps.close(); c.close();
1.首先PreparedStatement也是通过Connection对象调用方法preparedStatement,值得一提的是与createStatement()不同,参数不是为空,或者ResultType等设置,而必须是sql语句,可带?代表未定参数。
2.之后设置这些参数int型就用setInt()方法,字符串就用setString()方法,一般都是两个参数,第一个代表是前面sql中的第几个?,第二个是具体值。
3.最后PreparedStatement执行直接调用executeQuery()方法,无需任何参数即可返回ResultSet对象,之后的步骤和Statement就相同了。
比较Statement和PreparedStatement来说,后者会更加安全,而且多次使用会提高效率。但是单次执行来说前者会是更优的选择。