67、Java JDBC 数据库编程全解析

Java JDBC 数据库编程全解析

在 Java 编程中,使用 JDBC(Java Database Connectivity)进行数据库操作是一项重要的技能。下面将详细介绍如何让 JDBC 示例程序正常工作,以及一些相关的注意事项和更复杂的示例。

让示例程序正常工作

要让 JDBC 示例程序正常工作,需要完成以下几个步骤:
1. 找到 JDBC 驱动 :程序中包含 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); 语句,但在某些 JDK 1.1 安装中,可能找不到 JdbcOdbcDriver.class 文件。如果加载语句不起作用,可能是 Java 版本更改导致名称改变,需要重新查阅文档。可以通过注释掉加载语句之后到 catch 子句之前的代码来测试驱动加载是否正常,如果程序没有抛出异常,则说明驱动加载正常。
2. 配置数据库 :以 32 位 Windows 系统为例,具体步骤如下:
- 打开控制面板,选择“32bit ODBC”图标。
- 在打开的选项卡对话框中,对于 JDBC - ODBC 桥,重要的是在“System DSN”中设置数据库,同时为了测试配置和创建查询,还需要在“File DSN”中设置数据库。
- 选择一个已有的数据库,例如将多年维护的“people”数据库导出为逗号分隔的 ASCII 文件(通常扩展名为 .csv )。在“System DSN”部分选择“Add”,选择文本驱动来处理该文件,并取消勾选“use current directory”,指定导出数据文件的目录。
- 数据库通常表示为单个目录下的一组文件,每个文件通常包含一个表。只包含单个表的数据库称为平面文件数据库,而需要多个表通过连接来产生所需结果的数据库称为关系数据库。
3. 测试配置 :可以通过运行 JDBC 程序示例,直到 Connection c = DriverManager.getConnection(dbUrl, user, password); 语句。如果抛出异常,则说明配置不正确。也可以使用查询生成工具,如 Microsoft Query(随 Microsoft Office 提供)。该工具需要知道数据库的位置,需要在 ODBC 管理员的“File DSN”选项卡中添加新条目,指定文本驱动和数据库所在目录。
4. 生成 SQL 查询 :使用查询工具(如 Microsoft Query)创建查询。以搜索姓氏为“Eckel”且有电子邮件地址的记录为例,步骤如下:
- 启动新查询并使用查询向导,选择“people”数据库。
- 选择“people”表,选择 FIRST LAST EMAIL 列。
- 在“Filter Data”中,选择 LAST 并选择“equals”,参数为“Eckel”,点击“AND”单选按钮。
- 选择 EMAIL 并选择“Is not Null”。
- 在“Sort By”中选择 FIRST
- 点击 SQL 按钮,将自动生成正确的 SQL 代码:

SELECT people.FIRST, people.LAST, people.EMAIL
FROM people.csv people
WHERE (people.LAST='Eckel') AND  
(people.EMAIL Is Not Null)
ORDER BY people.FIRST
  1. 修改并粘贴查询 :由于查询工具使用了全限定名称,对于只涉及一个表的查询,可以选择移除大部分名称的“people”限定符:
SELECT FIRST, LAST, EMAIL
FROM people.csv people
WHERE (LAST='Eckel') AND  
(EMAIL Is Not Null)
ORDER BY FIRST

为了使程序能够根据命令行参数搜索不同的姓氏,将 SQL 语句改为动态创建的字符串:

"SELECT FIRST, LAST, EMAIL " +
"FROM people.csv people " +
"WHERE " +
"(LAST='" + args[0] + "') " +
" AND (EMAIL Is Not Null) " +
"ORDER BY FIRST";
GUI 版本的查找程序

为了更方便地使用查找程序,可以创建一个 GUI 版本。以下是示例代码:

//: c15:jdbc:VLookup.java
// GUI version of Lookup.java.
// <applet code=VLookup
// width=500 height=200></applet>
import javax.swing.*;  
import java.awt.*; 
import java.awt.event.*; 
import javax.swing.event.*; 
import java.sql.*; 
import com.bruceeckel.swing.*; 

public class VLookup extends JApplet { 
    String dbUrl = "jdbc:odbc:people"; 
    String user = ""; 
    String password = ""; 
    Statement s; 
    JTextField searchFor = new JTextField(20); 
    JLabel completion =  
        new JLabel("                        "); 
    JTextArea results = new JTextArea(40, 20); 
    public void init() { 
        searchFor.getDocument().addDocumentListener( 
            new SearchL()); 
        JPanel p = new JPanel(); 
        p.add(new Label("Last name to search for:")); 
        p.add(searchFor); 
        p.add(completion); 
        Container cp = getContentPane(); 
        cp.add(p, BorderLayout.NORTH); 
        cp.add(results, BorderLayout.CENTER); 
        try { 
            // Load the driver (registers itself) 
            Class.forName( 
                "sun.jdbc.odbc.JdbcOdbcDriver"); 
            Connection c = DriverManager.getConnection( 
                dbUrl, user, password); 
            s = c.createStatement(); 
        } catch(Exception e) { 
            results.setText(e.toString()); 
        } 
    } 
    class SearchL implements DocumentListener { 
        public void changedUpdate(DocumentEvent e){} 
        public void insertUpdate(DocumentEvent e){ 
            textValueChanged(); 
        } 
        public void removeUpdate(DocumentEvent e){ 
            textValueChanged(); 
        } 
    } 

    public void textValueChanged() { 
        ResultSet r; 
        if(searchFor.getText().length() == 0) { 
            completion.setText(""); 
            results.setText(""); 
            return; 
        } 
        try { 
            // Name completion: 
            r = s.executeQuery( 
                "SELECT LAST FROM people.csv people " + 
                "WHERE (LAST Like '" + 
                searchFor.getText()  +  
                "%') ORDER BY LAST"); 
            if(r.next())  
                completion.setText( 
                    r.getString("last")); 
            r = s.executeQuery( 
                "SELECT FIRST, LAST, EMAIL " + 
                "FROM people.csv people " + 
                "WHERE (LAST='" +  
                completion.getText() + 
                "') AND (EMAIL Is Not Null) " + 
                "ORDER BY FIRST"); 
        } catch(Exception e) { 
            results.setText( 
                searchFor.getText() + "\n"); 
            results.append(e.toString()); 
            return;  
        } 
        results.setText(""); 
        try { 
            while(r.next()) { 
                results.append( 
                    r.getString("Last") + ", "  
                    + r.getString("fIRST") +  
                    ": " + r.getString("EMAIL") + "\n"); 
            } 
        } catch(Exception e) { 
            results.setText(e.toString()); 
        } 
    } 
    public static void main(String[] args) { 
        Console.run(new VLookup(), 500, 200); 
    } 
} ///:~

这个程序添加了名称补全功能,当在文本框中输入姓氏时,会自动查找以该输入开头的姓氏,并显示第一个匹配的姓氏。然后根据该姓氏进行查询并显示结果。

为什么 JDBC API 看起来如此复杂

浏览 JDBC 的在线文档时,可能会觉得它很复杂。特别是 DatabaseMetaData 接口,包含了很多方法,如 dataDefinitionCausesTransactionCommit() getMaxColumnNameLength() 等。这是因为数据库从一开始就处于不断变化的状态,虽然有 SQL 标准,但存在很多变体。JDBC 提供了庞大的 DatabaseMetaData 接口,以便代码能够发现当前连接的特定“标准”SQL 数据库的功能。可以编写简单、可移植的 SQL,但如果要优化速度,就需要深入研究特定数据库的功能,代码量会大幅增加。

更复杂的示例:社区兴趣数据库(CID)

一个更复杂的示例是社区兴趣数据库(Community Interests Database,CID),它是一个位于服务器上的多表数据库,用于提供社区活动的存储库,并允许人们报名参加这些活动。以下是相关的代码和说明:
1. 数据库连接信息 :将数据库驱动、URL、用户名和密码放在一个单独的类中:

//: c15:jdbc:CIDConnect.java
// Database connection information for 
// the community interests database (CID). 

public class CIDConnect { 
    // All the information specific to CloudScape: 
    public static String dbDriver =  
        "COM.cloudscape.core.JDBCDriver"; 
    public static String dbURL = 
        "jdbc:cloudscape:d:/docs/_work/JSapienDB"; 
    public static String user = ""; 
    public static String password = ""; 
} ///:~
  1. 数据库表结构 :数据库由多个表组成,包含社区成员信息、活动信息、活动地点信息以及成员与活动的关联信息。以下是创建这些表的 SQL 字符串:
//: c15:jdbc:CIDSQL.java
// SQL strings to create the tables for the CID. 

public class CIDSQL { 
    public static String[] sql = { 
        // Create the MEMBERS table: 
        "drop table MEMBERS", 
        "create table MEMBERS " + 
        "(MEM_ID INTEGER primary key, " + 
        "MEM_UNAME VARCHAR(12) not null unique, "+ 
        "MEM_LNAME VARCHAR(40), " + 
        "MEM_FNAME VARCHAR(20), " + 
        "ADDRESS VARCHAR(40), " + 
        "CITY VARCHAR(20), " + 
        "STATE CHAR(4), " + 
        "ZIP CHAR(5), " + 
        "PHONE CHAR(12), " + 
        "EMAIL VARCHAR(30))", 
        "create unique index " + 
        "LNAME_IDX on MEMBERS(MEM_LNAME)", 
        // Create the EVENTS table 
        "drop table EVENTS", 
        "create table EVENTS " + 
        "(EVT_ID INTEGER primary key, " + 
        "EVT_TITLE VARCHAR(30) not null, " + 
        "EVT_TYPE VARCHAR(20), " + 
        "LOC_ID INTEGER, " + 
        "PRICE DECIMAL, " + 
        "DATETIME TIMESTAMP)", 
        "create unique index " + 
        "TITLE_IDX on EVENTS(EVT_TITLE)", 
        // Create the EVTMEMS table 
        "drop table EVTMEMS", 
        "create table EVTMEMS " + 
        "(MEM_ID INTEGER not null, " + 
        "EVT_ID INTEGER not null, " + 
        "MEM_ORD INTEGER)", 
        "create unique index " + 
        "EVTMEM_IDX on EVTMEMS(MEM_ID, EVT_ID)", 
        // Create the LOCATIONS table 
        "drop table LOCATIONS", 
        "create table LOCATIONS " + 
        "(LOC_ID INTEGER primary key, " + 
        "LOC_NAME VARCHAR(30) not null, " + 
        "CONTACT VARCHAR(50), " + 
        "ADDRESS VARCHAR(40), " + 
        "CITY VARCHAR(20), " + 
        "STATE VARCHAR(4), " + 
        "ZIP VARCHAR(5), " + 
        "PHONE CHAR(12), " + 
        "DIRECTIONS VARCHAR(4096))", 
        "create unique index " + 
        "NAME_IDX on LOCATIONS(LOC_NAME)", 
    }; 
} ///:~
  1. 创建数据库表 :以下程序使用 CIDConnect CIDSQL 信息加载 JDBC 驱动,连接到数据库,并创建上述表结构:
//: c15:jdbc:CIDCreateTables.java
// Creates database tables for the 
// community interests database. 
import java.sql.*; 

public class CIDCreateTables { 
    public static void main(String[] args)  
    throws SQLException, ClassNotFoundException, 
    IllegalAccessException { 
        // Load the driver (registers itself) 
        Class.forName(CIDConnect.dbDriver); 
        Connection c = DriverManager.getConnection( 
            CIDConnect.dbURL, CIDConnect.user,  
            CIDConnect.password); 
        Statement s = c.createStatement(); 
        for(int i = 0; i < CIDSQL.sql.length; i++) { 
            System.out.println(CIDSQL.sql[i]); 
            try { 
                s.executeUpdate(CIDSQL.sql[i]); 
            } catch(SQLException sqlEx) { 
                System.err.println( 
                    "Probably a 'drop table' failed"); 
            } 
        } 
        s.close(); 
        c.close(); 
    } 
} ///:~

第一次运行该程序时,“drop table”命令可能会失败,会捕获并报告异常,但忽略该异常。这样做是为了方便实验,可以修改定义表的 SQL 并重新运行程序,用新表替换旧表。
4. 加载和测试数据库 :以下程序将一些示例数据加载到数据库中,并进行查询测试:

//: c15:jdbc:LoadDB.java
// Loads and tests the database. 
import java.sql.*; 

class TestSet { 
    Object[][] data = { 
        { "MEMBERS", new Integer(1), 
            "dbartlett", "Bartlett", "David", 
            "123 Mockingbird Lane", 
            "Gettysburg", "PA", "19312", 
            "123.456.7890",  "bart@you.net" }, 
        { "MEMBERS", new Integer(2), 
            "beckel", "Eckel", "Bruce", 
            "123 Over Rainbow Lane", 
            "Crested Butte", "CO", "81224", 
            "123.456.7890", "beckel@you.net" }, 
        { "MEMBERS", new Integer(3), 
            "rcastaneda", "Castaneda", "Robert", 
            "123 Downunder Lane", 
            "Sydney", "NSW", "12345", 
            "123.456.7890", "rcastaneda@you.net" }, 
        { "LOCATIONS", new Integer(1), 
            "Center for Arts", 
            "Betty Wright", "123 Elk Ave.", 
            "Crested Butte", "CO", "81224", 
            "123.456.7890", 
            "Go this way then that." }, 
        { "LOCATIONS", new Integer(2), 
            "Witts End Conference Center", 
            "John Wittig", "123 Music Drive", 
            "Zoneville", "PA", "19123", 
            "123.456.7890", 
            "Go that way then this." }, 
        { "EVENTS", new Integer(1), 
            "Project Management Myths", 
            "Software Development", 
            new Integer(1), new Float(2.50), 
            "2000-07-17 19:30:00" }, 
        { "EVENTS", new Integer(2), 
            "Life of the Crested Dog", 
            "Archeology", 
            new Integer(2), new Float(0.00), 
            "2000-07-19 19:00:00" }, 
        // Match some people with events 
        {  "EVTMEMS",  
            new Integer(1),  // Dave is going to 
            new Integer(1),  // the Software event. 
            new Integer(0) }, 
        { "EVTMEMS",  
            new Integer(2),  // Bruce is going to 
            new Integer(2),  // the Archeology event. 
            new Integer(0) }, 
        { "EVTMEMS",  
            new Integer(3),  // Robert is going to 
            new Integer(1),  // the Software event. 
            new Integer(1) }, 
        { "EVTMEMS",  
            new Integer(3), // ... and  
            new Integer(2), // the Archeology event. 
            new Integer(1) }, 
    }; 
    // Use the default data set: 
    public TestSet() {} 
    // Use a different data set: 
    public TestSet(Object[][] dat) { data = dat; } 
} 

public class LoadDB { 
    Statement statement; 
    Connection connection; 
    TestSet tset; 
    public LoadDB(TestSet t) throws SQLException { 
        tset = t; 
        try { 
            // Load the driver (registers itself) 
            Class.forName(CIDConnect.dbDriver); 
        } catch(java.lang.ClassNotFoundException e) { 
            e.printStackTrace(System.err); 
        } 
        connection = DriverManager.getConnection( 
            CIDConnect.dbURL, CIDConnect.user,  
            CIDConnect.password); 
        statement = connection.createStatement(); 
    } 
    public void cleanup() throws SQLException { 
        statement.close(); 
        connection.close(); 
    } 
    public void executeInsert(Object[] data) { 
        String sql = "insert into "  
            + data[0] + " values("; 
        for(int i = 1; i < data.length; i++) { 
            if(data[i] instanceof String) 
                sql += "'" + data[i] + "'"; 
            else 
                sql += data[i]; 
            if(i < data.length - 1) 
                sql += ", "; 
        } 
        sql += ')'; 
        System.out.println(sql); 
        try { 
            statement.executeUpdate(sql); 
        } catch(SQLException sqlEx) { 
            System.err.println("Insert failed."); 
            while (sqlEx != null) { 
                System.err.println(sqlEx.toString()); 
                sqlEx = sqlEx.getNextException(); 
            } 
        }  
    } 
    public void load() { 
        for(int i = 0; i< tset.data.length; i++) 
            executeInsert(tset.data[i]); 
    } 
    // Throw exceptions out to console: 
    public static void main(String[] args)  
    throws SQLException { 
        LoadDB db = new LoadDB(new TestSet()); 
        db.load(); 
        try { 
            // Get a ResultSet from the loaded database: 
            ResultSet rs = db.statement.executeQuery( 
                "select " + 
                "e.EVT_TITLE, m.MEM_LNAME, m.MEM_FNAME "+ 
                "from EVENTS e, MEMBERS m, EVTMEMS em " + 
                "where em.EVT_ID = 2 " + 
                "and e.EVT_ID = em.EVT_ID " + 
                "and m.MEM_ID = em.MEM_ID"); 
            while (rs.next()) 
                System.out.println( 
                    rs.getString(1) + "  " +  
                    rs.getString(2) + ", " + 
                    rs.getString(3)); 
        } finally { 
            db.cleanup(); 
        } 
    } 
} ///:~

TestSet 类包含一个默认的数据集,可以使用不同的数据集创建 TestSet 对象。 executeInsert() 方法使用运行时类型识别(RTTI)来区分字符串数据和非字符串数据,构建 SQL 命令并发送到数据库。 LoadDB 类的构造函数建立数据库连接, load() 方法遍历数据集并调用 executeInsert() 插入每条记录, cleanup() 方法关闭语句和连接。数据库加载完成后,使用 executeQuery() 语句生成一个示例结果集,该查询是一个连接多个表的示例。

通过以上步骤和示例,你可以掌握使用 JDBC 进行数据库操作的基本方法,包括简单的查询和复杂的多表操作。同时,了解了 JDBC API 复杂的原因以及如何处理不同数据库的差异。在实际开发中,可以根据具体需求选择合适的数据库和查询方式,以提高程序的性能和可维护性。

Java JDBC 数据库编程全解析

不同场景下的 JDBC 应用分析
简单查询与复杂查询的区别

在前面的示例中,我们看到了简单查询和复杂查询的不同实现。简单查询通常只涉及单个表,如在“people”数据库中搜索特定姓氏的记录:

SELECT FIRST, LAST, EMAIL
FROM people.csv people
WHERE (LAST='Eckel') AND  
(EMAIL Is Not Null)
ORDER BY FIRST

而复杂查询可能涉及多个表的连接,如在社区兴趣数据库(CID)中查询参加特定活动的成员信息:

select 
e.EVT_TITLE, m.MEM_LNAME, m.MEM_FNAME 
from EVENTS e, MEMBERS m, EVTMEMS em 
where em.EVT_ID = 2 
and e.EVT_ID = em.EVT_ID 
and m.MEM_ID = em.MEM_ID

简单查询的优点是代码简单、易于理解和维护,适用于对单个表进行基本的数据检索。复杂查询则可以处理更复杂的业务逻辑,通过连接多个表来获取更全面的数据,但代码复杂度和性能开销相对较高。

性能优化考虑

当使用 JDBC 进行数据库操作时,性能优化是一个重要的考虑因素。以下是一些性能优化的建议:
1. 合理使用索引 :在数据库表中创建适当的索引可以加快查询速度。例如,在 CID 数据库中,为 MEMBERS 表的 MEM_LNAME 列创建了唯一索引:

create unique index 
LNAME_IDX on MEMBERS(MEM_LNAME)

这样在根据姓氏进行查询时,数据库可以更快地定位到相关记录。
2. 批量操作 :如果需要插入或更新大量数据,使用批量操作可以减少与数据库的交互次数,提高性能。例如,可以使用 PreparedStatement addBatch() executeBatch() 方法。
3. 避免不必要的查询 :在编写代码时,要确保只查询需要的数据,避免查询过多的列或记录。可以使用 SELECT 语句指定需要的列,而不是使用 SELECT *

JDBC 操作流程总结

为了更清晰地展示 JDBC 操作的流程,我们可以用以下 mermaid 流程图表示:

graph TD
    A[加载 JDBC 驱动] --> B[建立数据库连接]
    B --> C[创建 Statement 对象]
    C --> D{执行 SQL 语句}
    D -->|查询| E[获取 ResultSet 结果集]
    D -->|更新| F[获取受影响的行数]
    E --> G[处理结果集]
    F --> H[处理更新结果]
    G --> I[关闭 ResultSet]
    H --> I
    I --> J[关闭 Statement]
    J --> K[关闭数据库连接]
常见问题及解决方法

在使用 JDBC 进行数据库操作时,可能会遇到一些常见的问题,以下是一些问题及解决方法:
|问题|原因|解决方法|
|----|----|----|
|驱动加载失败|驱动类名错误、驱动文件未找到|检查驱动类名是否正确,确保驱动文件存在于类路径中|
|数据库连接失败|数据库 URL、用户名或密码错误,数据库服务未启动|检查数据库连接信息是否正确,确保数据库服务已启动|
|SQL 语句执行异常|SQL 语法错误、表名或列名错误|检查 SQL 语句的语法,确保表名和列名正确|

总结与展望

通过本文的介绍,我们详细了解了使用 JDBC 进行数据库操作的方法,包括让示例程序正常工作的步骤、GUI 版本的查找程序、JDBC API 复杂的原因以及更复杂的多表数据库示例。在实际开发中,我们可以根据具体需求选择合适的数据库和查询方式,同时注意性能优化和常见问题的处理。

随着技术的不断发展,JDBC 也在不断演进,未来可能会有更多的优化和改进。同时,也会有更多的数据库连接框架和工具出现,帮助开发者更高效地进行数据库操作。但无论如何,掌握 JDBC 的基本原理和操作方法仍然是非常重要的,它是进行 Java 数据库编程的基础。希望本文能对大家在使用 JDBC 进行数据库开发时有所帮助。

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样与统计,通过模拟系统元件的故障与修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构与设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码与案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行与可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理与实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估与优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值