“核心技术Ⅱ:数据库编程”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第258行: 第258行:


=== 分析SQL异常 ===
=== 分析SQL异常 ===
SQL 异常按照层次结构树的方式组织到了一起:
: [[File:SQL异常类型.png|700px]]
# '''SQLException:异常'''
#: 每个“'''SQLException'''”都有一个由多个“SQLException”对象构成的链,这些对象可以通过“'''getNextException'''”方法获取。
#: 这个异常链是每个异常都具有的由“'''Throwable'''”对象构成的“成因”链之外的异常链,因此,需要用两个嵌套的循环来完整枚举所有的异常。JavaSE6后“SQLException”类实现了“'''Iterable<Throwable>'''”接口,其“'''iterator()'''”方法可以产生一个“Iterator<Throwable>”,这个迭代器可以迭代这两个链,首先迭代第一个“SQLException”的成因链,然后迭代下一个“SQLException”,以此类推。
## 使用下面这个改进的 for 循环:
##: <syntaxhighlight lang="java">
for (Throwable t : sqlException)
{
    // do something with t
}
</syntaxhighlight>
## 调用“'''getSQLState'''”和“'''getErrorCode'''”方法来进一步分析它:
##: “getSQLState”将产生符合 X/Open 或 SQL:2003 标准的字符串(调用“'''DatabaseMetaData'''”接口的“'''getSQLStateType'''”方法可以查出驱动程序所使用的标准);
##: 错误代码是与具体的提 供商相关的;
# '''SQLWarning:警告'''
#: 数据库驱动程序可以将非致命问题作为警告报告,可以从“Connection”、“Statement”和“ResultSet”中获取这些警告。与SQL异常类似,警告也是串成链的。
#* “SQLWarning”类是“SQLException”的子类。(但“SQLWarning”不会被当作异常抛出)
## 循环获取 SQLWarning:
##: <syntaxhighlight lang="java">
SQLWarning w = stat.getWarning();
while (w != null)
{
    // do something with w
    w = w.nextWarning();
}
</syntaxhighlight>
## 调用“'''getSQLState'''”和“'''getErrorCode'''”来获取有关警告的更多信息;
#* 当数据从数据库中读出并意外被截断时,SQLWarning的“'''DataTruncation'''”子类就派上用场了。如果数据截断发生在更新语句中,那么“DataTruncation”将会被当作异常抛出。【???】
'''相关方法:'''
{| class="wikitable" style="width:100%;"
|-
! colspan="2"  style="text-align:left;"| java.sql.SQLException 1.1
|-
| style="width:50%;" | SOLException getNextException()
| style="width:50%;" | 返同链接到该SQL异常的下一个SQL异常,或者在到达链尾时返回null。
|-
| style="width:50%;" | Iterator<Throwabl e> iterator() 6
| style="width:50%;" | 获取迭代器,可以迭代链接的SQL异常和它们的成因。
|-
| style="width:50%;" | String getSQLState()
| style="width:50%;" | 获取“SQL状态”,即标准化的错误代码。
|-
| style="width:50%;" | int getErrorCode()
| style="width:50%;" | 获取提供商相关的错误代码。
|-
! colspan="2"  style="text-align:left;"| java.sql.SQLWarning 1.1
|-
| style="width:50%;" | SQLWarning getNextWarning()
| style="width:50%;" | 返回链接到该警告的下一个警告,或者在到达链尾时返回 null。
|-
! colspan="2"  style="text-align:left;"| java.sql.Connection 1.1
|-
! colspan="2"  style="text-align:left;"| java.sql.Statement 1.1
|-
! colspan="2"  style="text-align:left;"| java.sql.ResultSet 1.1
|-
| style="width:50%;" | SQLWarning getWarnings()
| style="width:50%;" | 返回未处理警告中的第一个,或者在没有未处理警告时返回 null。
|-
! colspan="2"  style="text-align:left;"| java.sql.DataTruncation 1.1
|-
| style="width:50%;" | boolean getParameter()
| style="width:50%;" | 如果在参数上进行了数据截断,则返回 true;如果在列上进行了数据截断,则返回 false。
|-
| style="width:50%;" | int getIndex()
| style="width:50%;" | 返回被截断的参数或列的索引。
|-
| style="width:50%;" | int getDataSize()
| style="width:50%;" | 返回'''应该被传输'''的字节数量,或者在该值未知的情况下返回 -1。
|-
| style="width:50%;" | int getTransferSize()
| style="width:50%;" | 返回'''实际被传输'''的字节数量,或者在该值未知的情况下返回 -1。
|}
=== 组装数据库 ===
=== 组装数据库 ===



2021年1月10日 (日) 06:27的版本


关于JDBC

Java数据库连接(JDBC)API:

遗循了微软公司非常成功的ODBC模式(ODBC:微软提供为C语言访问数据库的一套编程接口)。JDBC和ODBC都基千同一个思想:根据API编写的程序都可以与驱动管理器进行通信,而驱动管理器则通过驱动程序与实际的数据库进行通信。


JDBC的目标:

  1. 通过使用标准的SQL语句,甚至是专门的SQL扩展,程序员就可以利用Java语言开发访问数据库的应用,同时还依旧遵守Java语言的相关约定。
  2. 数据库供应商和数据库工具开发商可以提供底层的驱动程序。因此,他们可以优化各自数据库产品的驱动程序。


JDBC 驱动程序类型:

  1. 笫1类驱动程序:将JDBC翻译成ODBC, 然后使用一个ODBC驱动程序与数据库进行通信。
    较早版本的Java包含一个这样的驱动程序:JDBC/ODBC桥,不过在使用这个桥接器之前需要对ODBC进行相应的部署和正确的设置。(Java 8已经不再提供JDBC/ODBC桥)
  2. 笫2类驱动程序:由部分Java程序和部分本地代码组成的,用于与数据库的客户端API进行通信。
    在使用这种驱动程序之前,客户端不仅斋要安装Java类库,还需要安装一些与平台相关的代码。
  3. 第3类驱动程序:纯Java客户端类库,它使用一种与具体数据库无关的协议将数据库请求发送给服务器构件,然后该构件再将数据库请求翻译成数据库相关的协议。这简化了部署,因为平台相关的代码只位于服务器端。
  4. 第4类驱动程序:纯Java类库,它将JDBC请求直接翻译成数据库相关的协议。


JDBC 的典型用法:

  1. 传统的客户端/服务器模型:通常是在服务器端部署数据库,而在客户端安装富GUI程序。(在此模型中,JDBC驱动程序应该部署在客户端)
    JDBC:传统的客户端-服务器应用.png
  2. 三层模型:客户端不直接调用数据库,而是调用服务器上的中间件层,由中间件层完成数据库查询操作。
    JDBC:三层结构的应用.png


结构化查询语言:SQL【SQL,读作“ [ˈsiːkwəl] ”】

SQL数据类型.png

JDBC配置

  1. 驱动程序JAR文件):
    需要准备对应的数据库驱动包:在运行访问数据库的程序时,需要将驱动程序的 JAR 文件包括到类路径中(编译时并不需要这个 JAR 文件)。
    // 在从命令行启动程序时,只需要使用下面的命令:
    java -classpath driverPath:. ProgramName
    
  2. 注册驱动器类
    1. 自动注册:许多JDBC的JAR文件会自动注册驱动器类,则可以跳过手动注册步骤。
      • (包含“META-INF/services/java.sql.Driver”文件的 JAR 文件可以自动注册驱动器类)
      • 自动注册对于遵循 JDBC4 的驱动程序是必须具备的特性。
    2. 手动注册:通过“DriverManager”来注册驱动器。
      1. 在 Java 程序中加载驱动器类:(注册驱动器的静态初始化器)
        Class.forName("org.postgresql.Driver"); // force loading of driver class
        
      2. 设置“jdbc.drivers”属性:
        • 命令行参数指定这个属性:
          java -Djdbc.drivers=org.postgresql.Driver ProgramName
          
        • 或,应用中通过调用设置系统属性:
          System.setProperty("jdbc.drivers", "org.postgresql.Driver");
          
        • 在这种方式中可以提供多个驱动器,用冒号将它们分隔开:
          org.postgresq1.Driver:org.apache.derby.jdbc.ClientDriver
          
  3. 连接到数据库
    1. 在代码中打开数据库连接:
      String url = "jdbc:postgresql :COREJAVA"; 
      String username = "dbuser"; 
      String password = "secret"; 
      Connection conn = DriverManager.getConnection(url, username, password);
      
      驱动管理器遍历所有注册过的驱动程序,以便找到一个能够使用数据库 URL 中指定的子协议的驱动程序。
    2. 使用数据库配置文件(如“database.properties”)
      jdbc.drivers=org.postgresql.Oriver
      jdbc.url=jdbc:postgresql:COREJAVA
      jdbc.username=dbuser
      jdbc.password=secret
      


  • 数据库驱动器名称:如:
    org.apache.derby.jdbc.ClientDriver
    org.postgresql.Driver
    com.mysql.jbdc.driver
    
  • 数据库URL:JDBCURL 的一般语法为:“jdbc:subprotocol:other_stuff”,其中subprotocol用于选择连接到数据库的具体驱动程序。
    URL包含各种与数据库类型相关的参数,例如“主机名”、“端口号”和“数据库名”。如:
    jdbc:derby://1ocalhost:1527/COREJAVA;create=true
    jdbc:postgresq1:COREJAVA
    


示例:

	try{
		Class.forName("com.mysql.jbdc.driver");
		Connection conn = DriverManager.getConnection("jdbc:mysql:///spring_day01","root","admin");

		String sql = "select * from users where username = ?";
		PrepareStatement pre = conn.prepareStatement(sql);
		pre.setString(1,"小陈");
    
		ResultSet rs = pre.executeQuery();
		while(rs.next()){
			String username = rs.getString("username");
			String password = rs.getString("password");
	
			User user = new User();
			user.setUsername(username);
			user.setPassword(password);

			System.out.println(user);
		}
	}catch(Exception ex){
		ex.printStackTrace();
	}finally{
		rs.close();
		pre.close();
		conn.close();
	}

使用JDBC语句

执行SQL语句

执行语句:

  1. executeUpdate”:返回受SQL语句影响的行数,或者对不返同行数的语句返回0。
    • 既可以执行诸如“INSERT”、“UPDATE”和“DELETE”之类的操作,也可以执行诸如“CREATETABLE”和“DROPTABLE”之类的数据定义语句。
  2. executeQuery”:返回一个“ResultSet”类型的对象,可以通过它来每次一行地迭代遍历所有查询结果。
    • 执行“SELECT”查询时必须使用executeQuery方法。
  3. execute”:可能会产生多个结果集和更新计数。
    如果第一个执行结果是结果集,则返回 true;反之,返回 false。调用“getResultSet”或“getUpdateCount”方法可以得到第一个执行结果。
    • 可以执行任意的SQL语句。(通常只用于由用户提供的交互式查询)


结果处理:分析结果集:

while (rs.next()) 
{
   String username = rs.getString("username");
   String password = rs.getString("password");
   int age = rs.getInt("age");
	
   User user = new User();
   user.setUsername(username);
   user.setPassword(password);
   user.setAge(age);

   System.out.println(user);
}
  • ResultSet接口的迭代协议与“java.util.Iterator”接口稍有不同:
    对于ResultSet接口,迭代器初始化时被设定在第一行之前的位置,必须调用“next”方法将它移动到第一行。另外,它没有“hasNext”方法,我们需要不断地调用“next”,直至该方法返回 false 。
  1. 不同的数据类型有不同的访问器,比如“getString”和“getDouble”。
    当 get 方法的类型和列的数据类型不一致时,每个 get 方法都会进行合理的类型转换。例如,调用“rs.getString("Price")”时,该方法会将 Price 列的浮点值转换成字符串。
  2. 每个访问器都有两种形式:
    1. 一种接受数字型参数。(列序号从1开始)如,“rs.getString(l)”返回的是当前行中第一列的值;
    2. 另一种接受字符串参数。如上例;


相关方法:

java.sql.DriverManager 1.1
static Connection getConnection(String url, String user, String password) 建立一个到指定数据库的连接,并返回一个 Connection 对象。
java.sql.Connection 1.1
Statement createStatement() 创建一个 Statement 对象,用以执行不带参数的 SQL 查询和更新。
void close() 立即关闭当前的连接,并释放由它所创建的JDBC资源。
java.sql.Statement 1.1
ResultSet executeQuery(String sqlQuery) 执行给定字符串中的 SQL 语句,并返回一个用于查看查询结果的 ResultSet 对象。
int executeUpdate(String sqlStatement) 执行字符串中指定的“INSERT”、“UPDATE”或“DELETE”等 SQL 语句。还可以执行数据定义语言 (Data Definition Language, DDL) 的语句,如“CREATE TABLE”。返回受影响的行数,如果是没有更新计数的语句,则返回 0。
long executeLargeUpdate(String sqlStatement) 8
boolean execute(String sql Statement) 执行字符串中指定的 SQL 语句。可能会产生多个结果集和更新计数。如果第一个执行结果是结果集,则返回 true;反之,返回 false。调用“getResultSet”或“getUpdateCount”方法可以得到第一个执行结果。【见“多结果集”一节】
ResultSet getResultSet() 返回前一条查询语句的结果集。如果前一条语句未产生结果媒,则返回 null 值。对于每一条执行过的语句,该方法只能被调用一次。
int getUpdateCount() 返回受前一条更新语句影响的行数。如果前一条语句未更新数据库,则返回-1。对于每一条执行过的语句,该方法只能被调用一次。
1ong getLargeUpdateCount() 8
void close() 关闭 Statement 对象以及它所对应的结果集。
boolean isClosed() 6 如果语句被关闭,则返回true。
void closeOnCompletion() 7 使得一旦该语句的所有结果集都被关闭,则关闭该语句。
java.sql.ResultSet 1.1
boolean next() 将结果集中的当前行向前移动一行。如果已经到达最后一行的后面,则返同false。注意,初始请况下必须调用该方法才能转到第一行
Xxx getXxx(int columnNumber) 用给定的列序号或列标签返回或更新该列的值,并将值转换成指定的类型。列标签是SQL的AS子句中指定的标签,在没有使用AS时,它就是列名。
Xxx getXxx(String columnLabel)

(Xxx指数据类型, 例如“int”、“double”、“String”和“Date”等)

<T> T getObject(int columnIndex, Class<T> type) 7
<T> T getObject(String columnLabel, Class<T> type) 7
void updateObject(int columnIndex, Object x, SQLType targetSqlType) 8
void updateObject(String columnLabel, Object x, SQLType targetSqlType) 8
int findColumn(String columnName) 根据给定的列名,返回该列的序号。
void close() 立即关闭当前的结果集。
boolean isClosed() 6 如果该语句被关闭,则返回true。

Connection、Statement和结果集

  1. 每个“Connection”对象都可以创建一个或多个“Statement”对象。(1:N)
    调用“Connection”类的“close”方法将关闭该连接上的所有语句。
  2. 同一个“Statement”对象可以用于多个不相关的命令和查询。 (1:N)
  3. 一个“Statement”对象最多只能有一个打开的结果集。(如果需要执行多个查询操作,且需要同时分析查询结果,那么必须创建多个 Statement对象)
    如果“Statement”对象上有一个打开的结果集,那么调用“close”方法将自动关闭该结果。
    • 在JavaSE 7,可以在“Statement”上调用“closeOnCompletion”方法,在其所有结果集都被关闭后,该语句会立即被自动关闭。


  • 至少有一种常用的数据库(Microsoft SQL Server)的JDBC驱动程序只允许同时存在一个活动的Statement对象。
    使用“DatabaseMetaData”接口中的“getMaxStatements”方法可以获取JDBC驱动程序支持的同时活动的语句对象的总数。
  • 使用完“ResultSet”、“Statement”或“Connection”对象后,应立即调用“close”方法。
    如果所用连接都是短时的,那么无需考虑关闭语句和结果集。只需将“close”语句放在带资源的try语句中,以便确保最终连接对象不可能继续保持打开状态:
    try (Connection conn = . . .) 
    {
        Statement stat = conn.createStatement();
        ResultSet result = stat.executeQuery(queryString);
        // process query result 
    }
    

分析SQL异常

SQL 异常按照层次结构树的方式组织到了一起:

SQL异常类型.png
  1. SQLException:异常
    每个“SQLException”都有一个由多个“SQLException”对象构成的链,这些对象可以通过“getNextException”方法获取。
    这个异常链是每个异常都具有的由“Throwable”对象构成的“成因”链之外的异常链,因此,需要用两个嵌套的循环来完整枚举所有的异常。JavaSE6后“SQLException”类实现了“Iterable<Throwable>”接口,其“iterator()”方法可以产生一个“Iterator<Throwable>”,这个迭代器可以迭代这两个链,首先迭代第一个“SQLException”的成因链,然后迭代下一个“SQLException”,以此类推。
    1. 使用下面这个改进的 for 循环:
      for (Throwable t : sqlException) 
      {
          // do something with t 
      }
      
    2. 调用“getSQLState”和“getErrorCode”方法来进一步分析它:
      “getSQLState”将产生符合 X/Open 或 SQL:2003 标准的字符串(调用“DatabaseMetaData”接口的“getSQLStateType”方法可以查出驱动程序所使用的标准);
      错误代码是与具体的提 供商相关的;
  2. SQLWarning:警告
    数据库驱动程序可以将非致命问题作为警告报告,可以从“Connection”、“Statement”和“ResultSet”中获取这些警告。与SQL异常类似,警告也是串成链的。
    • “SQLWarning”类是“SQLException”的子类。(但“SQLWarning”不会被当作异常抛出)
    1. 循环获取 SQLWarning:
      SQLWarning w = stat.getWarning();
      while (w != null) 
      {
          // do something with w 
          w = w.nextWarning();
      }
      
    2. 调用“getSQLState”和“getErrorCode”来获取有关警告的更多信息;
    • 当数据从数据库中读出并意外被截断时,SQLWarning的“DataTruncation”子类就派上用场了。如果数据截断发生在更新语句中,那么“DataTruncation”将会被当作异常抛出。【???】


相关方法:

java.sql.SQLException 1.1
SOLException getNextException() 返同链接到该SQL异常的下一个SQL异常,或者在到达链尾时返回null。
Iterator<Throwabl e> iterator() 6 获取迭代器,可以迭代链接的SQL异常和它们的成因。
String getSQLState() 获取“SQL状态”,即标准化的错误代码。
int getErrorCode() 获取提供商相关的错误代码。
java.sql.SQLWarning 1.1
SQLWarning getNextWarning() 返回链接到该警告的下一个警告,或者在到达链尾时返回 null。
java.sql.Connection 1.1
java.sql.Statement 1.1
java.sql.ResultSet 1.1
SQLWarning getWarnings() 返回未处理警告中的第一个,或者在没有未处理警告时返回 null。
java.sql.DataTruncation 1.1
boolean getParameter() 如果在参数上进行了数据截断,则返回 true;如果在列上进行了数据截断,则返回 false。
int getIndex() 返回被截断的参数或列的索引。
int getDataSize() 返回应该被传输的字节数量,或者在该值未知的情况下返回 -1。
int getTransferSize() 返回实际被传输的字节数量,或者在该值未知的情况下返回 -1。

组装数据库

执行查询操作

预备语句 读写LOB SQL 转义 多结果集 获取自动生成的键

可滚动和可更新的结果集

可滚动的结果集 可更新的结果集

行集

构建行集 被缓存的行集

元数据

事务

用JDBC对事务编程 保存点 批量更新

高级SQL类型

Web与企业应用中的连接管理