第十三章.MySQL数据库与JDBC编程(下)

Posted lanshanxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十三章.MySQL数据库与JDBC编程(下)相关的知识,希望对你有一定的参考价值。

JDBC的典型用法:

  JDBC4.2常用接口和类简介:

    DriverManager:用于管理JDBC驱动的服务类,程序中使用该类的主要功能是获取Connection对象,该类包含如下方法:

      public static synchronized Connection getConnection(String url, String user, String  pass) throws SQLException:该方法获得url对应数据库的连接

    Connection:代表数据库连接对象,每个Connection代表一个物理连接会话。想要访问数据库,必须先获得数据库连接,该接口的常用方法如下:

      1.Statement createStatement() throws SQLException:该方法返回一个Statement对象。

      2.PreparedStatement prepareStatement(String sql) throws SQLException:该方法返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译。

      3.CallableStatement prepareCall(String sql) throws SQLException:该方法返回CallableStatement对象,该对象用于调用存储过程

    上面三个方法都返回用于执行SQL语句的Statement对象,PreparedStatement、CallableStatement是Statement的子类,只有获得了Statement之后才可执行SQL语句。

    除此之外,Connection还有如下几个用于控制事务的方法:

      1.Savepoint setSavepoint():创建一个保存点

      2.Savepoint setSavepoint(String name):以指定名字创建一个保存点

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

      4.void rollback():回滚事务

      5.void rollback(Savepoint savepoint):将事务回滚到指定的保存点

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

      7.void commit():提交事务

    Java7 为Connection新增了

      setSchema(String schema)、getSchema()两个方法:用于控制该Connection访问的数据库Schema

      setNetworkTimeout(Executor executor, int milliseconds)、getNetworkTimeout()两个方法:用于控制数据库连接超时行为。

    Statement:用于执行SQL语句的工具接口,常用方法如下:

      1.ResultSet executeQuery(String sql) throws SQLException:该方法用于执行查询语句,并返回查询结果对应的ResultSet对象。该方法只能用于执行查询语句

      2.int executeUpdate(String sql) throws SQLException:该方法用于执行DML(数据操作语言)语句,并返回受影响的行数;该方法也可用于执行DDL(数据定义

       语言)语句执行DDL语句将返回0

      3.boolean execute(String sql) throws SQLException:该方法可执行任何SQL语句。若执行后第一个结果为ResultSet对象,则返回true;若执行后第一个结果为受影

       响的行数或没有任何结果,则返回false。

    Java7为Statement新增了closeOnCompletion()方法:若Statement执行了该方法,则当所有依赖于该Statement的ResultSet关闭时,该Statement会自动关闭。

    Java7还为Statement提供了isCloseOnCompletion()方法:用于判断该Statement是否打开了“closeOnCompletion”.

    Java8为Statement新增了多个重载的executeLargeUpdate()方法:这些方法相当于增强版的executeUpdate()方法,返回值类型为long,即当DML语句影响的记录条数超过

     Integer.MAX_VALUE时,就应该使用executeLargeUpdate()方法。

    PreparedStatement:预编译的Statement对象。PreparedStatement是Statement的子接口,它允许数据库预编译SQL语句(这些SQL语句通常带有参数),以后每次只

     改变SQL命令的参数,避免数据库每次都需要编译SQL语句,因此性能更好。相对于Statement而言,使用PreparedStatement执行SQL语句时,无需再传入SQL语句,只

     要为预编译的SQL语句传入参数数值即可。所以它比Statement多了如下方法:

      1.void setXxx(int parameterIndex, Xxx value):该方法根据传入参数值的类型不同,需要使用不同的方法。传入的值根据索引传给SQL语句中指定位置的参数。

  ResultSet:结果集对象。该对象包含访问查询结果的方法,ResultSet可以通过列索引或列名获得列数据。它包含了如下常用方法来移动记录指针:

    1.void close():释放ResultSet对象。

    2.boolean absolute(int row):将结果集的记录指针移动到第row行,若row是负数,则移动到倒数第row行。若移动后的记录指针指向一条有效记录,则该方法返回true

    3.void beforeFirst():将ResultSet的记录指针定位到首行之前,这是ResultSet结果集记录指针的初始状态——记录指针的起始位置位于第一行之前

    4.boolean first():将ResultSet的记录指针定位到首行。若移动后的记录指针指向一条有效记录,则该方法返回true。

    5.boolean previous():将ResultSet的记录指针定位到上一行。若移动后的记录指针指向一条有效记录,则该方法返回true。

    6.boolean next():将ResultSet的记录指针定位到下一行,若移动后的记录指针指向一条有效记录,则该方法返回true。

    7.boolean last():将ResultSet的记录指针定位到最后一行,若移动后的记录指针指向一条有效记录,则该方法返回true。 

    8.void afterLast():将ResultSet的记录指针定位到最后一行之后。

JDBC编程步骤:

  1.加载数据库驱动:

    通常使用Class类的forName()静态方法来加载驱动:

      Class.forName(driverClass);//driverClass就是数据库驱动类所对应的字符串。如:加载mysql的驱动代码

      Class.forName("com.mysql.jdbc.Driver");

  2.通过DriverManager获取数据库连接:

    //获取数据库连接

    DriverManager.getConnection(String url, String user, String pass);//数据库URL、登录数据库的用户名和密码。

    数据库URL通常遵循如下写法:

    jdbc:subprotocol:other stuff

    jdbc是固定的写法,subprotocol指定连接到特定数据库的驱动,other stuff也不是固定的(由数据库定)

    MySQL数据库的URL写法如下:

    jdbc:mysql://hostname:port/databasename

  3.通过Connection对象创建Statement对象。有如下三个方法:

    1.createStatement():创建基本的Statement对象

    2.prepareStatement(String sql):根据传入的SQL语句创建预编译的Statement对象

    3.prepateCall(String sql):根据传入的SQL语句创建CllableStatement对象

  4.使用Statement执行SQL语句。有如下三个方法:

    1.execute():可执行任何SQL语句,但比较麻烦

    2.executeUpdate():主要用于执行DML和DDL语句。执行DML语句返回受SQL语句影响的行数,执行DDL语句返回0

    3.executeQuery():只能执行查询语句,执行后返回代表查询结果的ResultSet对象

  5.操作结果集:

    若执行SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。程序可通过操作该ResultSet对象来取出查询结果:

      1.next()、previous()、first()、last()、beforeFirst()、afterLast()、absolute()等移动记录指针的方法

      2.getXxx()方法获取记录指针指向行、特定列的值。该方法既可使用列索引作为参数,也可使用列名作为参数。使用列索引作为参数性能更好,使用列名作为参数可

       读性更好

  6.回收数据库资源:

    包括关闭ResultSet、Statement和Connection等资源。

  下面程序简单示范了JDBC编程,并通过ResultSet获得结果集的过程:

 1 drop database if exists select_test;
 2 create database select_test;
 3 use select_test;
 4 # 为了保证从表参照的主表存在,通常应该先建主表。
 5 create table teacher_table
 6 (
 7     # auto_increment:实际上代表所有数据库的自动编号策略,通常用作数据表的逻辑主键。
 8     teacher_id int auto_increment,
 9     teacher_name varchar(255),
10     primary key(teacher_id)
11 );
12 create table student_table
13 (
14     # 为本表建立主键约束
15     student_id int auto_increment primary key,
16     student_name varchar(255),
17     # 指定java_teacher参照到teacher_table的teacher_id列
18     java_teacher int,
19     foreign key(java_teacher) references teacher_table(teacher_id)
20 );
21 insert into teacher_table
22 values
23 (null , \'Yeeku\');
24 insert into teacher_table
25 values
26 (null , \'Leegang\');
27 insert into teacher_table
28 values
29 (null , \'Martine\');
30 insert into student_table
31 values
32 (null , \'张三\' , 1);
33 insert into student_table
34 values
35 (null , \'张三\' , 1);
36 insert into student_table
37 values
38 (null , \'李四\' , 1);
39 insert into student_table
40 values
41 (null , \'王五\' , 2);
42 insert into student_table
43 values
44 (null , \'_王五\' , 2);
45 
46 insert into student_table
47 values
48 (null , null , 2);
49 insert into student_table
50 values
51 (null , \'赵六\' , null);
数据库建表语句

将驱动(mysql-connector-java-5.1.30-bin.jar)放到java目录的下的jre/lib/ext/目录下面。或者将驱动的路径添加到classpath环境变量后面。

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.Statement;
 4 import java.sql.ResultSet;
 5 
 6 public class ConnMySql{
 7     public static void main(String[] args) throws Exception{
 8         //1.加载驱动,使用反射知识,现在记住这么写
 9         Class.forName("com.mysql.jdbc.Driver");
10         try(
11             //2.使用DriverManager获取数据库连接
12             //其中返回的Connection就代表了Java程序和数据库的连接
13             //不同数据库的URL写法需要查询驱动文档,用户名、密码由DBA分配
14             Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/select_test", "root", "123456");
15             //3.使用Connection来创建一个Statement对象
16             Statement stmt = conn.createStatement();
17             //4.执行SQL语句
18             /*
19             Statement有三种执行SQL语句的方法:
20                 1.execute()可执行任何SQL语句-返回一个boolean值,若执行后第一个结果是ResultSet,则返回true,否则返回false
21                 2.executeQuery()执行select语句-返回查询到的结果集
22                 3.executeUpdate()用于执行DML语句-返回一个整数,代表被SQL语句影响的记录条数
23             */
24             ResultSet rs = stmt.executeQuery("select s.*, teacher_name"
25                 + " from student_table s , teacher_table t"
26                 + " where t.teacher_id = s.java_teacher")){
27                     //ResultSet有一系列的getXxx(列索引 | 列名)方法,用于获取记录指针
28                     //指向行、特定列的值,不断地使用next()将记录指针下移一行
29                     //若移动之后记录指针依然指向有效行,则next()方法返回true
30                     while(rs.next()){
31                         System.out.println(rs.getInt(1) + "\\t"
32                             + rs.getString(2) + "\\t"
33                             + rs.getString(3) + "\\t"
34                             + rs.getString(4));
35                     }
36                 }
37     }
38 }
View Code

 执行SQL语句的方式:

  使用Java8新增的executeLargeUpdate()方法执行DDL和DML语句:

  使用statement执行DDL和DML语句的步骤与执行普通查询语句的步骤基本相似,区别在于执行了DDL语句后返回值为0,执行了DML语句后返回值为受到影响的记录条数。

  MySQL暂不支持executeLargeUpdate()方法。所以我们使用executeUpdate()方法。

  下面的程序并没有直接把数据库连接信息写在代码中,而是使用一个mysql.ini文件(就是properties文件)来保存数据库连接信息,这是比较成熟的做法——当需要把应用程

   序从开发环境移植到生产环境时,无需修改源代码,只需要修改mysql.ini配置文件即可:

 mysql.ini文件内容:

mysql.ini和ExecuteDDL.java文件所放位置:

 1 import java.io.FileInputStream;
 2 import java.util.Properties;
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.Statement;
 6 
 7 
 8 public class ExecuteDDL{
 9     private String driver;
10     private String url;
11     private String user;
12     private String pass;
13     public void initParam(String paramFile) throws Exception{
14         //使用Properties类来加载属性文件
15         Properties props = new Properties();
16         props.load(new FileInputStream(paramFile));
17         driver = props.getProperty("driver");
18         url = props.getProperty("url");
19         user = props.getProperty("user");
20         pass = props.getProperty("pass");
21     }
22     
23     public void createTable(String sql) throws Exception{
24         //加载驱动
25         Class.forName(driver);
26         try(
27             //获取数据库连接
28             Connection conn = DriverManager.getConnection(url, user, pass);
29             //使用Connection来创建一个Statement对象
30             Statement stmt = conn.createStatement()){
31                 //执行DDL语句,创建数据表
32                 stmt.executeUpdate(sql);
33             }
34     }
35     
36     public static void main(String[] args) throws Exception{
37         ExecuteDDL ed = new ExecuteDDL();
38         ed.initParam("mysql.ini");
39         ed.createTable("create table jdbc_test "
40             + "( jdbc_id int auto_increment primary key, "
41             + "jdbc_name varchar(255), "
42             + "jdbc_desc text);");
43             System.out.println("------建表成功------");
44     }
45 }
View Code

  使用executeUpdate()方法执行DML语句:

    和上面程序的步骤是一样的,只不过程序代码需要修改:

 1 import java.io.FileInputStream;
 2 import java.util.Properties;
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.Statement;
 6 
 7 public class ExecuteDML{
 8     private String driver;
 9     private String url;
10     private String user;
11     private String pass;
12     
13     public void initParam(String paramFile) throws Exception{
14         Properties props = new Properties();
15         props.load(new FileInputStream(paramFile));
16         driver = props.getProperty("driver");
17         url = props.getProperty("url");
18         user = props.getProperty("user");
19         pass = props.getProperty("pass");
20     }
21     
22     public int insertData(String sql) throws Exception{
23         //加载驱动
24         Class.forName(driver);
25         try(
26             //获取数据库连接
27             Connection conn = DriverManager.getConnection(url, user, pass);
28             //使用Connection来创建一个Statement对象
29             Statement stmt = conn.createStatement()){
30                 //执行SQL语句,返回受影响的记录条数
31                 return stmt.executeUpdate(sql);
32             }
33     }
34     
35     public static void main(String[] args) throws Exception{
36         ExecuteDML ed = new ExecuteDML();
37         ed.initParam("mysql.ini");
38         int result = ed.insertData("insert into jdbc_test(jdbc_name,jdbc_desc)"
39             + "select s.student_name , t.teacher_name "
40             + "from student_table s , teacher_table t "
41             + "where s.java_teacher = t.teacher_id;");
42         System.out.println("------系统中一共有" + result + "条记录受影响------");
43     }
44 }
View Code

  使用execute()方法执行SQL语句:

    Statement的execute()方法几乎可以执行任何SQL语句,但它执行SQL语句比较麻烦,通常没有必要使用execute()方法来执行SQL语句。

    使用execute()方法执行SQL语句的返回值只是boolean值,它表明执行该SQL语句是否返回了ResultSet对象,Statement提供了如下两个方法来获取执行结果:

      1.getResultSet():获取该Statement执行查询语句所返回的ResultSet对象

      2.getUpdateCount():获取该Statement执行DML语句所影响的记录行数。

    下面程序示范了使用Statement的execute()方法来执行任意的SQL语句:

 1 import java.util.Properties;
 2 import java.io.FileInputStream;
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.Statement;
 6 import java.sql.ResultSet;
 7 import java.sql.ResultSetMetaData;
 8 
 9 public class ExecuteSQL{
10     private String driver;
11     private String url;
12     private String user;
13     private String pass;
14     
15     public void initParam(String paramFile) throws Exception{
16         //使用Properties类来加载属性文件
17         Properties props = new Properties();
18         props.load(new FileInputStream(paramFile));
19         driver = props.getProperty("driver");
20         url = props.getProperty("url");
21         user = props.getProperty("user");
22         pass = props.getProperty("pass");
23     }
24     
25     public void executeSql(String sql) throws Exception{
26         //加载数据库驱动
27         Class.forName(driver);
28         try(
29             //获取数据库连接
30             Connection conn = DriverManager.getConnection(url, user, pass);
31             //通过Connection创建一个Statement
32             Statement stmt = conn.createStatement()){
33                 //执行SQL语句,返回boolean值表示是否包含ResultSet
34                 boolean hasResultSet = stmt.execute(sql);
35                 //若执行后有ResultSet结果集
36                 if(hasResultSet){
37                     try(
38                         //获取结果集
39                         ResultSet rs = stmt.getResultSet()){
40                             //ResultSetMetaData是用于分析结果集的元数据接口
41                             ResultSetMetaData rsmd = rs.getMetaData();
42                             int columnCount = rsmd.getColumnCount();
43                             //迭代输出ResultSet对象
44                             while(rs.next()){
45                                 //依次输出每列的值
46                                 for(int i = 0; i < columnCount; i++){
47                                     System.out.print(rs.getString(i + 1) + "\\t");
48                                 }
49                                 System.out.print("\\n");
50                             }
51                         }
52                 }else{
53                     System.out.println("该SQL语句影响的记录有" + stmt.getUpdateCount() + "条");
54                 }
55             }
56     }
57     
58     public static void main(String[] args) throws Exception{
59         ExecuteSQL es = new ExecuteSQL();
60         es.initParam("mysql.ini");
61         System.out.println("------执行删除表的DDL语句------");
62         es.executeSql("drop table if exists my_test");
63         System.out.print("------执行建表的DDL语句------");
64         es.executeSql("create table my_test"
65             + "(test_id int auto_increment primary key, "
66             + "test_name varchar(255))");
67         System.out.println("------执行插入数据的DML语句------");
68         es.executeSql("insert into my_test(test_name) "
69             + "select student_name from student_table");
70         System.out.println("------执行查询数据的查询语句------");
71         es.executeSql("select * from my_test");
72     }
73 }
View Code

  从结果看,执行DDL语句显示受影响记录条数;执行DML显示插入、修改、删除的记录条数;执行查询语句可以输出查询结果。

  上面程序获得的SQL执行结果是没有根据各列的数据类型调用相应的getXxx()方法,而是直接使用getString()方法来取得值,这是可以的。

  ResultSet的getString()方法几乎可以获取除了Blob之外的任意类型列的值,因为所有的数据类型都可以自动转换成字符串类型。

  使用PreparedStatement执行SQL语句:

    若经常要反复执行一条结构相似的SQL语句,如下两条:

    insert into student_table values(null, \'张三\', 1);

    insert into student_table values(null, \'李四\', 2);

    对于这两条语句,它们结构相似,只是执行插入时插入的值不同而已。对于这种情况,可以使用占位符(?)参数的SQL语句来代替它:

    insert into student_table values(null, ?, ?);

    JDBC提供了PreparedStatement接口,它是Statement的子接口。它可以进行预编译SQL语句,预编译后的SQL语句被存储在PreparedStatement对象中,然后可以使用该

     对象多次高效地执行该语句。使用PreparedStatement比使用Statement的效率要高。

    创建PreparedStatement对象使用Connection的prepareStatement()方法,该方法需要传入一个SQL字符串,该SQL字符串可以包括占位符参数,如下:

      //创建一个PreparedStatement对象

      pstmt = conn.prepareStatement("insert into student_table values(null,?,1)");

    PreparedStatement也提供了execute()、executeUpdate()、executeQuery()三个方法来执行SQL语句,不过这三个方法无需参数,因为PreparedStatement已经存储了预

     编译的SQL语句。

    使用PreparedStatement预编译SQL语句时,该SQL语句可以带占位符参数,因此在执行SQL语句之前必须为这些参数传入参数值,PreparedStatement提供了一系列

     的setXxx(int index, Xxx value)方法来传入参数值。

 下面程序示范了使用Statement和PreparedStatement分别插入100条记录的对比。:

 1 import java.io.FileInputStream;
 2 import java.util.Properties;
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.PreparedStatement;
 6 import java.sql.Statement;
 7 
 8 public class PreparedStatementTest{
 9     private String driver;
10     private String url;
11     private String user;
12     private String pass;
13     
14     public void initParam(String paramFile) throws Exception{
15         //使用Properties类加载属性文件
16         Properties props = new Properties();
17         props.load(new FileInputStream(paramFile));
18         driver = props.getProperty("driver");
19         url = props.getProperty("url");
20         user = props.getProperty("user");
21         pass = props.getProperty("pass");
22         //加载驱动
23         Class.forName(driver);
24     }
25     
26     public void insertUseStatement() throws Exception{
27         long start = System.currentTimeMillis();
28         try(
29             //获取数据库连接
30             Connection conn = DriverManager.getConnection(url, user, pass);
31             //使用Connection来创建一个Statement对象
32             Statement stmt = conn.createStatement())
33             {
34                 //需要使用100条SQL语句来插入100条记录
35                 for(int i = 0; i < 100; i++){
36                     stmt.executeUpdate("insert into student_table values("
37                         + "null,\'姓名" + i + "\', 1)");
38                 }
39                 System.out.println("使用Statement费时:"
40                     + (System.currentTimeMillis() - start));
41             }
42     }
43     
44     public void insertUsePrepare() throws Exception{
45         long start = System.currentTimeMillis();
46         try(
47             //获取数据库连接
48             Connection conn = DriverManager.getConnection(url, user, pass);
49             //使用Connection来创建一个PreparedStatement对象
50             PreparedStatement pstmt = conn.prepareStatement("insert into student_table values(null,?,1)"))
51             {
52                 //100次为PreparedStatement的参数设值,就可以插入100条记录

以上是关于第十三章.MySQL数据库与JDBC编程(下)的主要内容,如果未能解决你的问题,请参考以下文章

第十三章:Python の 网络编程进阶

《On Java 8》中文版 第十三章 函数式编程

MySQL必知必会第十三章 分组数据

C#图解教程 第十三章 委托

第十三章 MySQL操作

C Primer Plus(第六版)第十三章 编程练习答案