82、Jython数据库编程全解析

Jython数据库编程全面解析

Jython数据库编程全解析

1. DBM文件

DBM文件是哈希数据库文件,其行为与字典对象类似。不过,这种相似性存在一定局限,因为当前实现并未使用所有字典方法,且所有DBM键和值都必须是字符串。虽然有多种DBM工具可与Jython配合使用,但目前Jython仅捆绑了dumbdbm。它的名字可能不太吸引人,实现简单且速度较慢,但仍是一个较为有效的DBM克隆。

以下是使用dumbdbm模块的简单交互示例:

>>> import dumbdbm 
>>> dbm = dumbdbm.open("dbmtest") 
>>> dbm['Value 1'] = 'A' 
>>> dbm['Value 2'] = 'B' 
>>> dbm['Value 3'] = 'B' 
>>> for key in dbm.keys(): 
...     print key, ":", dbm[key] 
... 
Value 3 : B 
Value 2 : B 
Value 1 : A 
>>> 
>>> dir(dbm.__class__) 
['__delitem__', '__doc__', '__getitem__', '__init__', '__len__', 
'__module__', '__setitem__', '_addkey', '_addval', '_commit', '_setval', 
'_update', 'close', 'has_key', 'keys'] 
>>> 
>>> dbm.close() 

通过 dir(dbm.__class__) 方法可以看出,dbm对象与字典有所不同。在字典方法中,仅实现了 keys has_key 方法,不过字典对象所需的最小特殊方法( __setitem__ __delitem__ __getitem__ __len__ )确实存在。DBM对象的一个显著优势是具有持久性,重新打开 dbmtest 目录可显示之前输入的值。

打开新的dumbdbm目录实际上会创建两个文件:一个扩展名为 .dir ,另一个扩展名为 .dat 。在 open 函数中无需指定扩展名,只需提供目录名称。其他DBM实现允许在 open 函数中使用标志和模式参数,但dumbdbm会忽略这些参数。

2. 序列化

序列化是将对象转换为适合传输或存储的流。存储方面使得该主题与数据库相关,因为序列化对象通常存储在数据库中。要序列化Jython对象,可使用 marshal pickle cPickle 模块。

  • marshal模块 :该模块可序列化代码对象和内置数据对象,但它常被 cPickle 模块取代,不过在某些项目中仍会出现。需要注意的是, marshal 模块对CPython的一个吸引力在于其能够编译代码对象,但目前Jython对象不具备此功能,因此该模块的使用较少。
  • marshal 模块有四个方法: dump dumps load loads dump load 方法用于将对象序列化到文件或从文件中恢复对象,文件对象必须以二进制模式打开。 dumps loads 方法用于将对象序列化到字符串或从字符串中恢复对象。
    ```python

    import marshal
    file = open(“myMarshalledData.dat”, “w+b”) # 必须为二进制模式
    L = range(30, 50, 7)
    marshal.dump(L, file) # 将对象L序列化到文件

    file.flush() # 转储后刷新或关闭以确保完整性
    file.seek(0) # 将文件指针移到开头以便加载

    restoredL = marshal.load(file)
    print L
    [30, 37, 44]
    print restoredL
    [30, 37, 44]
    ```

  • pickle和cPickle模块 :这两个模块是序列化Jython对象的首选方法。它们的区别仅在于实现和性能,使用方法相同。大多数对象(除Java对象、代码对象以及具有复杂 __getattr__ __setattr__ 方法的Jython对象外)都可以使用 pickle cPickle 进行序列化。 cPickle 模块是 pickle 模块的高性能Java版本。
方法 描述
Dump(object, file[, bin]) 将内置数据对象序列化到先前打开的文件对象。若对象无法序列化,将引发 PicklingError 异常。可使用文本或二进制格式,第三个参数为零或缺失表示文本格式,非零表示二进制格式。
Load(file) 从先前打开的文件对象中读取并反序列化数据。若以二进制模式写入数据,提供给 load 的文件对象也必须以二进制模式打开。
dumps(object[, bin]) 将对象序列化为字符串对象,而非文件。若对象无法序列化,将引发 PicklingError 异常。第二个参数为零或缺失表示文本格式,非零表示二进制格式。
loads(string) 将字符串反序列化为数据对象。

以下是使用 cPickle 模块存储 product 类实例的示例:

>>> import cPickle 
>>> class product: 
...     def __init__(self, productCode, data): 
...         self.__dict__.update(data) 
...         self.productCode = productCode 
... 
>>> data = {'name':'widget', 'price':'112.95', 'inventory':1000} 
>>> widget = product('1123', data) 
>>> f = open(widget.productCode, 'wb') 
>>> cPickle.dump(widget, f, 1) 
>>> f.close() 

恢复该实例时,需要重新定义 product 类,并以二进制模式打开文件:

>>> import cPickle 
>>> class product: 
...     def __init__(self, productCode, data): 
...         self.__dict__.update(data) 
...         self.productCode = productCode 
... 
>>> f = open("1123", "rb") 
>>> widget = cPickle.load(f) 
>>> f.close() 
>>> vars(widget) 
{'name': 'widget', 'productCode': '1123', 'price': '112.95', 'inventory': 1000} 

对象可通过定义特殊方法 __getstate__ __setstate__ 来控制其序列化和反序列化过程。

你也可以使用 pickler unpickler 对象:

>>> import cPickle 
>>> f = open("picklertest", "w+b") 
>>> p = cPickle.Pickler(f) 
>>> u = cPickle.Unpickler(f) 
>>> 
>>> L = range(10) 
>>> p.dump(L)  # 使用pickler对象进行序列化 
>>> f.seek(0) 
>>> print u.load() 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

pickle 可以处理递归数据结构、包含自身引用的对象以及嵌套数据结构。

3. Shelves

Jython的 shelve 模块将DBM目录的便利性与 pickle 的序列化功能相结合,创建了持久对象存储。 shelve 的行为类似于字典,与 dumbdbm 模块类似,但不同之处在于 shelve 允许任何可 pickle 的对象作为值,不过键仍必须是字符串。

以下是使用 shelve 模块的交互示例:

>>> import shelve, dumbdbm 
>>> dbm = dumbdbm.open("dbmdata") 
>>> shelf = shelve.Shelf(dbm) 
>>> shelf['a list'] = range(10) 
>>> shelf['a dictionary'] = {1:1, 2:2, 3:3} 
>>> print shelf.keys() 
['a list', 'a dictionary'] 
>>> shelf.close() 

你也可以直接使用 shelve open 方法:

>>> import shelve 
>>> shelf = shelve.open("dbmdata") 
4. PythonObjectInputStream

在Java中序列化Jython对象的过程与序列化Java对象基本相同,唯一的区别是 org.python.util.PythonObjectInputStream 。通常,Jython对象在Java中可以按预期进行序列化和反序列化,但 PythonObjectInputStream 在反序列化继承自Java类的Jython对象时,有助于解析类。

以下是从Java序列化Jython对象的伪代码示例:

// 导入所需类 
import java.io.*; 
import org.python.core.*; 
// 标识资源 
String file = "someFileName"; 
// 进行序列化,创建一个OutputStream(这里是FileOutputStream) 
// 然后创建一个ObjectOutputStream 
FileOutputStream oStream = new FileOutputStream(file); 
ObjectOutputStream objWriter = new ObjectOutputStream(oStream); 
// 写入一个简单的Jython对象 
objWriter.writeObject(new PyString("some string")); 
// 清理 
objWriter.flush(); 
oStream.close(); 

反序列化对象的伪代码如下:

// 导入所需类 
import java.io.*; 
import org.python.core.*; 
import org.python.util.PythonObjectInputStream; 
// 标识资源 
String file = "someFileName"; 
// 打开一个InputStream(这里是FileInputStream) 
FileInputStream iStream = new FileInputStream(file); 
// 使用输入流打开一个ObjectInputStream或PythonObjectInputStream 
PythonObjectInputStream objReader = new PythonObjectInputStream(iStream); 
// 对于不继承Java类的对象可以使用以下方式 
// ObjectInputStream objReader = new ObjectInputStream(iStream); 
// 读取对象 
PyObject po = (PyObject)objReader.readObject(); 
// 清理 
iStream.close(); 
5. 数据库管理系统

本节详细介绍了如何使用Jython与MySQL和PostgreSQL数据库管理系统(DBMS)进行交互。Jython可以使用任何具有Java驱动程序的数据库,但选择这两个数据库是因为它们非常流行、免费、稳定、常用且有大量文档。

5.1 MySQL

MySQL是一个SQL数据库服务器,可在大多数类UNIX平台和Windows上运行。要运行相关示例,你需要下载并安装MySQL及其相关的JDBC驱动程序。MySQL可从 http://www.mysql.com/ 的“downloads”部分获取,MySQL JDBC驱动程序也位于该网站的同一下载部分,最新驱动程序和版本信息可在 http://mmmysql.sourceforge.net/ 获取。

安装完成后,你需要添加用户和数据库。以下是启动服务器的命令:
- *nix系统: /pathToMysql/bin/safe_mysqld &
- Windows系统: \pathToMysql\bin\winmysqladmin.exe

创建测试数据库的命令如下:

C:\mysql\bin>mysql 
Welcome to the MySQL monitor. Commands end with ; or \g. 
Your MySQL connection id is 3 to server version: 3.23.39 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 
mysql> CREATE DATABASE test; 
Query OK, 1 row affected (0.33 sec) 

确认数据库存在的命令:

mysql> SHOW DATABASES\g 
+----------+ 
| Database | 
+----------+ 
| mysql    | 
| test     | 
+----------+ 
2 rows in set (0.00 sec)mysql> 

添加用户 jyuser 并授予其对测试数据库的所有权限的命令:

mysql> USE test; 
mysql> GRANT ALL ON test TO jyuser@localhost IDENTIFIED BY "beans"; 
Query OK, 0 rows affected (0.05 sec) 
5.2 PostgreSQL

PostgreSQL是一个功能强大的对象关系数据库管理系统(ORDBMS),可在大多数类UNIX平台和Windows 2000/NT上运行。要运行相关示例,你需要使用PostgreSQL服务器和PostgreSQL JDBC驱动程序,可从 http://www.postgresql.org/ 获取。

在*nix系统上安装时,下载适合你系统的包格式并运行包管理器。在Windows 2000/NT系统上安装时,需要先下载Cygwin和cygipc包以模拟UNIX环境。

安装服务器后,需要初始化数据库集群:

initdb -D /path/to/datadirectory 

通常数据目录为 /usr/local/pgsql/data ,命令如下:

initdb -D /usr/local/pgsql/data 

在数据目录中,有一个 pg_hba.conf 文件,用于控制哪些主机可以连接到数据库。在使用JDBC驱动程序连接数据库之前,需要在该文件中为你要连接的机器添加主机条目。

创建用户 jyuser 的命令:

[shell prompt]$ createuser -U postgres -d -P jyuser 
Enter password for user "jyuser": 
Enter it again: 
Shall the new user be allowed to create more new users? (y/n) n 
CREATE USER 

创建测试数据库的命令:

[shell prompt]$ createdb -U jyuser test 
CREATE DATABASE 

启动服务器的命令:

postmaster -i -D /usr/local/pgsql/data & 
6. JDBC

Java使用JDBC和 java.sql 包与SQL数据库进行交互,这意味着Jython也可以使用这些。使用JDBC API和 java.sql 包与SQL数据库交互仅需要相应数据库的JDBC驱动程序(当然还需要数据库)。

6.1 连接数据库

数据库连接需要JDBC URL,通常还需要用户名和密码。

  • JDBC URL :URL是唯一标识数据库连接的字符串,语法为 jdbc:<subprotocol>:<subname> 。以下是MySQL和PostgreSQL数据库的JDBC URL语法:
  • MySQL: jdbc:mysql://[hostname][:port]/databaseName[parameter=value]
  • PostgreSQL: jdbc:postgresql://[hostname][:port]/databaseName[parameter=value]

例如,连接本地测试数据库的URL如下:
- MySQL: jdbc:mysql://localhost:3306/test
- PostgreSQL: jdbc:postgresql://localhost:5432/test

默认主机为 localhost ,默认端口MySQL为3306,PostgreSQL为5432,因此本地连接可简化为:
- MySQL: jdbc:mysql:///test
- PostgreSQL: jdbc:postgresql:///test

在JDBC URL末尾,你可以指定连接参数。以下是一些常见参数:
| 参数 | 含义 | 默认值 | 适用数据库 |
| — | — | — | — |
| User | 数据库用户名 | 无 | 两者 |
| password | 密码 | 无 | 两者 |
| autoReconnect | 连接中断时是否尝试重新连接(true|false) | false | MySQL |
| maxReconnects | 驱动程序尝试重新连接的次数(假设 autoReconnect 为true) | 3 | MySQL |
| initialTimeout | 重新连接前等待的秒数(假设 autoReconnect 为true) | 2 | MySQL |
| maxRows | 返回的最大行数(0表示所有行) | 0 | MySQL |
| useUnicode | 是否使用Unicode(true或false) | false | MySQL |
| characterEncoding | 使用的Unicode字符编码(如果 useUnicode 为true) | 无 | MySQL |

  • JDBC连接 :建立数据库连接的步骤如下:
    1. 将适当的驱动程序添加到类路径中。
    2. 将驱动程序注册到JDBC DriverManager。
    3. 向 java.sql.DriverManager.getConnection 方法提供JDBC URL、用户名和密码。

添加MySQL和PostgreSQL JDBC驱动程序到类路径的命令如下:

# 对于MySQL 
set CLASSPATH=\path\to\mm_mysql-2_0_4-bin.jar;%CLASSPATH% 
# 对于PostgreSQL 
set CLASSPATH=\path\to\jdbc7.1-1.2.jar;%CLASSPATH% 

注册驱动程序有两种基本方法:
- 使用 -D 开关在命令行设置 jdbc.drivers 属性。
- 使用 java.lang.Class.forName(classname) 方法。

以下是使用 Class.forName 方法加载MySQL和PostgreSQL驱动程序的示例:

import java 
try: 
    java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
    java.lang.Class.forName("org.postgresql.Driver") 
except java.lang.ClassNotFoundException: 
    print "No appropriate database driver found" 
    raise SystemExit 

连接数据库的方法有三种签名:

public static Connection getConnection(String url) throws SQLException 
public static Connection getConnection(String url, Properties info) throws SQLException 
public static Connection getConnection(String url, String user, String password) throws SQLException 

假设本地存在测试数据库,以 jyuser 用户和 beans 密码连接的可能语法如下:

from java.sql import DriverManager 
from java.util import Properties 
# 使用getConnection(URL)方法 
mysqlConn = DriverManager.getConnection(
    "jdbc:mysql://localhost/test?user=jyuser&password=beans") 
postgresqlConn = java.sql.DriverManager.getConnection(
    "jdbc:postgresql://localhost/test?user=jyuser&password=beans") 
# 使用getConnection(URL, Properties)方法 
params = Properties() 
params.setProperty("user", "jyuser") 
params.setProperty("password", "beans") 
mysqlConn = DriverManager.getConnection("jdbc:mysql://localhost/test", params) 
postgresqlConn = DriverManager.getConnection("jdbc:postgresql://localhost/test", params) 
# 使用getConnection(URL, String, String)方法 
mysqlConn = DriverManager.getConnection("jdbc:mysql://localhost/test", "jyuser", "beans") 
postgresqlConn = DriverManager.getConnection("jdbc:postgresql://localhost/test", "jyuser", "beans") 
6.2 连接脚本

Jython的交互式解释器是一个理想的数据库客户端工具。为了避免每次使用数据库时都手动输入数据库连接语句,可以使用Jython的 -i 命令行选项,或者将交互式控制台包装在所需的连接和关闭语句中。

以下是一个数据库客户端启动脚本示例:

# File: mystart.py 
from java.lang import Class 
from java.sql import DriverManager 
from org.python.util import InteractiveConsole 
import sys 
url = "jdbc:mysql://localhost/%s" 
user, password = "jyuser", "beans" 
# 注册驱动程序 
Class.forName("org.gjt.mm.mysql.Driver") 
# 允许通过命令行参数可选设置数据库 
if len(sys.argv) == 2: 
    url = url % sys.argv[1] 
else: 
    url = url % "test" 
# 获取连接 
dbconn = DriverManager.getConnection(url, user, password) 
# 创建语句 
try: 
    stmt = dbconn.createStatement() 
except: 
    dbconn.close() 
    raise SystemExit 
# 创建内部控制台 
interp = InteractiveConsole({"dbconn":dbconn, "stmt":stmt}, "DB Shell") 
try: 
    interp.interact("Jython DB client") 
finally: 
    # 关闭资源 
    stmt.close() 
    dbconn.close() 
    print 
6.3 连接对话框

另一种常见的连接任务是使用对话框获取连接信息。以下是一个数据库连接对话框的示例:

# File: DBLoginDialog.py 
import sys 
import java 
from java import awt 
from java import sql 
import pawt 
class DBLoginDialog(awt.Dialog): 
    '''DBLoginDialog提示用户输入数据库信息并建立数据库登录。注册一个连接接收器作为客户端。''' 
    def __init__(self, parentFrame, message): 
        awt.Dialog.__init__(self, parentFrame) 
        self.client = None 
        self.background = pawt.colors.lightgrey 
        bag = pawt.GridBag(self, anchor='WEST', fill='NONE') 
        # 创建所需组件 
        self.Hostname = HighlightTextField("localhost", 24) 
        self.DBMS = awt.Choice(itemStateChanged=self.updatePort) 
        self.Port = HighlightTextField("3306", 5) 
        self.Database = HighlightTextField("test",12) 
        self.User = HighlightTextField("jyuser", 10) 
        self.Password = HighlightTextField("", 10, echoChar='*') 
        # 填充选择组件选项 
        self.DBMS.add("MySQL") 
        self.DBMS.add("PostgreSQL") 
        # 添加消息 
        bag.addRow(awt.Label(message), anchor='CENTER') 
        # 将组件放入布局 
        for x in ["Hostname", "DBMS", "Port", "Database", 
                  "User", "Password"]: 
            bag.add(awt.Label(x + ":")) 
            bag.addRow(self.__dict__[x]) 
        # 添加动作按钮 
        bag.add(awt.Button("Login", actionPerformed=self.login), 
                fill='HORIZONTAL') 
        bag.addRow(awt.Button("Cancel", actionPerformed= self.close), 
                   anchor='CENTER') 
        self.pack() 
        bounds = parentFrame.bounds 
        self.bounds = (bounds.x + bounds.width/2 - self.width/2, 
                       bounds.y + bounds.height/2 - self.height/2, 
                       self.width, self.height) 
    def login(self, e): 
        db = self.DBMS.selectedItem 
        if db == "MySQL": 
            driver = "org.gjt.mm.mysql.Driver" 
        else: 
            driver = "org.postgresql.Driver" 
        try: 
            java.lang.Class.forName(driver) 
        except java.lang.ClassNotFoundException: 
            self.showError("Unable to load driver %s" % driver) 
            return 
        url = "jdbc:%s://%s:%s/%s" % (db.lower(), self.Hostname.text, 
                                       self.Port.text, 
                                       self.Database.text) 
        try: 
            dbcon = sql.DriverManager.getConnection(url, 
                                                    self.User.text, 
                                                    self.Password.text) 
            self.dispose() 
            self.client(dbcon) 
        except sql.SQLException: 
            self.showError("Unable to connect to database") 
    def updatePort(self, e): 
        if self.DBMS.selectedItem == 'MySQL': 
            port = '3306' 
        elif self.DBMS.selectedItem == 'PostgreSQL': 
            port = '5432' 
        self.Port.text= port 
    def setClient(client): 
        self.client = client 
    def showError(self, message): 
        d = awt.Dialog(self.parent, "Error") 
        panel = awt.Panel() 
        panel.add(awt.Label(message)) 
        button = awt.Button("OK") 
        panel.add(button) 
        d.add(panel) 
        d.pack() 
        bounds = self.parent.bounds 
        d.bounds = (bounds.x + bounds.width/2 -d.width/2, 
                    bounds.y + bounds.height/2 - d.height/2, 
                    d.width, d.height) 
        d.windowClosing = lambda e, d=d: d.dispose() 
        button.actionPerformed = d.windowClosing 
        d.visible = 1 
    def close(self, e): 
        self.dispose() 
class HighlightTextField(awt.TextField, awt.event.FocusListener): 
    def __init__(self, text, chars, **kw): 
        awt.TextField.__init__(self, text, chars) 
        self.addFocusListener(self) 
        for k in kw.keys(): 
            exec("self." + k + "=kw[k]") 
    def focusGained(self, e): 
        e.source.selectAll() 
    def focusLost(self, e): 
        e.source.select(0, 0) 
def dummyClient(connection): 
    if connection != None: 
        print "\nDatabase connection successfully received by client." 
        print "Connection=", connection 
        connection.close() 
        print "Database connection properly closed by client." 
if __name__ == '__main__': 
    # 创建一个虚拟框架作为对话框的父窗口 
    f = awt.Frame("DB Login", windowClosing=lambda e: sys.exit()) 
    screensize = f.toolkit.screenSize 
    f.bounds = (screensize.width/2 - f.width/2, 
                screensize.height/2 - f.height/2, 
                f.width, f.height) 
    # 创建并显示对话框窗口 
    dbi = DBLoginDialog(f, "Connection Information") 
    dbi.client = dummyClient 
    dbi.windowClosing = dbi.windowClosed = lambda e: sys.exit() 
    dbi.visible = 1 
6.4 数据库元数据

连接到数据库后,你可以探索数据库和连接的信息,即元数据。Java连接对象( java.sql.Connection )有一个 getMetaData 方法,返回 java.sql.DatabaseMetaData 对象,该对象提供了有关数据库连接的详细信息。

以下是一个交互式探索MySQL元数据的示例:

>>> import java 
>>> from java.sql import DriverManager 
>>> 
>>> # 注册驱动程序 
>>> java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 650329> 
>>> 
>>> # 获取连接 
>>> url, user, password = "jdbc:mysql:///test", "jyuser", "beans" 
>>> dbconn = DriverManager.getConnection(url, user, password) 
>>> 
>>> # 获取DatabaseMetaData对象 
>>> md = dbconn.getMetaData() 
>>> 
>>> # 使用元数据对象查找MySQL信息 
>>> print md.systemFunctions 
DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID, VERSION 
>>> md.numericFunctions 
'ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,FLOOR,LOG, 
LOG10,MAX,MIN,MOD,PI,POW,POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE' 
>>> md.supportsTransactions() 
0 
>>> 
>>> # 不要忘记关闭连接 
>>> dbconn.close() 

以下是一个比较MySQL和PostgreSQL元数据的脚本示例:

# File: jdbcMetaData.py 
from DBLoginDialog import DBLoginDialog 
from java import awt 
import sys 
class MetaData(awt.Frame): 
    def __init__(self): 
        self.windowClosing=lambda e: sys.exit() 
        self.databases = ["MySQL", "PostgreSQL"] 
        self.panel = awt.Panel() 
        self.infoArea = awt.TextArea("", 40, 60, 
                                     awt.TextArea.SCROLLBARS_VERTICAL_ONLY, 
                                     font=("Monospaced", awt.Font.PLAIN, 12)) 
        self.panel.add(self.infoArea) 
        self.add(self.panel) 
        self.pack() 
        screensize = self.toolkit.screenSize 
        self.bounds = (screensize.width/2 - self.width/2, 
                       screensize.height/2 - self.height/2, 
                       self.width, self.height) 
        self.data = {} 
        self.visible = 1 
        self.dialog = DBLoginDialog(self, "Select a Connection") 
        self.dialog.client = self.gatherMetaInfo 
        self.dialog.visible = 1 
    def showResults(self): 
        infoWidth = self.infoArea.columns 
        info = 'Method' + 'MySQL PostgreSQL\n'.rjust(infoWidth - 7) 
        info += '-' * (infoWidth - 1) + '\n' 
        keys = self.data.keys() 
        keys.sort() 
        for x in keys: 
            info += x 
            mysql = str(self.data[x]['MySQL']) 
            postgresql = str(self.data[x]['PostgreSQL']) 
            results = mysql.ljust(18 - len(postgresql)) + postgresql 
            info += results.rjust(self.infoArea.columns - len(x) - 2) 
            info += "\n" 
        self.infoArea.text = info 
    def nextDatabase(self): 
        if len(self.databases): 
            self.dialog.visible = 1 
        else: 
            self.showResults() 
    def gatherMetaInfo(self, dbconn): 
        if dbconn==None: 
            return 
        metaData = dbconn.getMetaData() 
        dbname = metaData.databaseProductName 
        for cmd in self.getCommands(): 
            value = "" 
            try: 
                exec("value = metaData.%s()" % cmd) 
            except: 
                value = "Test failed" 
            if self.data.has_key(cmd): 
                self.data[cmd][dbname] = value 
            else: 
                self.data.update({cmd:{dbname:value}}) 
        dbconn.close() # 关闭数据库 
        self.databases.remove(dbname) 
        self.nextDatabase() 
    def getCommands(self): 
        return ['getCatalogTerm', 'getMaxBinaryLiteralLength', 
                'getMaxCatalogNameLength', 'getMaxCharLiteralLength', 
                'getMaxColumnsInGroupBy', 'getMaxColumnsInIndex', 
                'getMaxColumnsInOrderBy', 'getMaxColumnsInSelect', 
                'getMaxColumnsInTable', 'getMaxConnections', 
                'getMaxCursorNameLength', 'getMaxIndexLength', 
                'getMaxProcedureNameLength', 'getMaxRowSize', 
                'getMaxStatementLength', 'getMaxStatements', 
                'getMaxTablesInSelect', 'getMaxUserNameLength', 
                'supportsANSI92EntryLevelSQL', 'supportsBatchUpdates', 
                'supportsCatalogsInDataManipulation', 
                'supportsCoreSQLGrammar', 
                'supportsDataManipulationTransactionsOnly', 
                'supportsDifferentTableCorrelationNames', 
                'supportsFullOuterJoins', 'supportsGroupByUnrelated', 
                'supportsMixedCaseQuotedIdentifiers', 
                'supportsOpenStatementsAcrossCommit', 
                'supportsOpenStatementsAcrossRollback', 
                'supportsPositionedDelete', 'supportsUnion', 
                'supportsSubqueriesInComparisons', 
                'supportsTableCorrelationNames', 'supportsTransactions'] 
if __name__ == '__main__': 
    md = MetaData() 
6.5 语句

与MySQL和PostgreSQL交互主要涉及执行SQL语句。要执行SQL语句,需要创建一个语句对象。

以下是创建语句对象的示例:

>>> import java 
>>> java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 1876475> 
>>> db, user, password = "test", "jyuser", "beans" 
>>> url = "jdbc:mysql://localhost/%s" % db 
>>> dbconn = java.sql.DriverManager.getConnection(url, user, password) 
>>> stmt = dbconn.createStatement() 

createStatement 方法还可以设置结果集的类型和并发参数。例如,创建一个可更新的结果集:

>>> import java 
>>> from java import sql 
>>> java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 1876475> 
>>> db, user, password = "test", "jyuser", "beans" 
>>> url = "jdbc:mysql://localhost/%s" % db 
>>> dbconn = sql.DriverManager.getConnection(url, user, password) 
>>> 
>>> type = sql.ResultSet.TYPE_SCROLL_SENSITIVE 
>>> concurrency = sql.ResultSet.CONCUR_UPDATABLE 
>>> stmt = dbconn.createStatement(type, concurrency) 

语句对象有三个方法用于执行SQL语句: execute() executeQuery() executeUpdate()
- execute() 方法返回0或1,表示是否有结果集。
- executeQuery() 方法始终返回一个结果集。
- executeUpdate() 方法返回一个整数,表示受影响的行数。

以下是创建表和插入数据的示例:

>>> query = "CREATE TABLE random ( number tinyint, letter char(1) )" 
>>> stmt.execute(query) 
0 
>>> import string, random 
>>> for i in range(20): 
... number = random.randint(0, 51) 
... query = "INSERT INTO random (number, letter) VALUES (%i, '%s')" 
... query = query % (number, string.letters[number - 1]) 
... stmt.executeUpdate(query) 
1 
6.6 结果集

使用 executeQuery 方法返回的对象是结果集( ResultSet )。结果集对象有许多方法用于导航记录和检索数据。

以下是查询 random 表并遍历结果的示例:

>>> import java 
>>> java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 1876475> 
>>> db, user, password = "test", "jyuser", "beans" 
>>> url = "jdbc:mysql://localhost/%s" % db 
>>> dbconn = java.sql.DriverManager.getConnection(url, user, password) 
>>> stmt = dbconn.createStatement() 
>>> rs = stmt.executeQuery("SELECT * FROM random") 
>>> while rs.next(): 
... print rs.getString('letter'), ": ", rs.getInt('number') 
... 
... 
U : 46 
K : 36 
h : 7 
Q : 42 
l : 11 
u : 20 
n : 13 
z : 25 
U : 46 
Y : 50 
j : 9 
o : 14 
i : 8 
s : 18 
d : 3 
A : 26 
K : 36 
j : 9 
n : 13 
g : 6 
>>> stmt.close() 
>>> dbconn.close() 

结果集的导航方法因JDBC版本和创建的语句类型而异。在JDBC 2.0之前,导航仅限于 next() 方法。当前JDBC版本支持 next() first() last() previous() absolute() relative() afterLast() beforeFirst() moveToCurrentRow() moveToInsertRow() 等方法。

结果集还可以更新行,但需要满足一定条件。例如,在MySQL中,结果集可更新的条件是查询仅包含一个表、不使用连接且选择了表的主键,并且语句的并发类型必须设置为 ResultSet.CONCUR_UPDATABLE

以下是更新表并进行滚动和更新操作的示例:

>>> import java 
>>> from java import sql 
>>> java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 1876475> 
>>> user, password = "jyuser", "beans" 
>>> url = "jdbc:mysql://localhost/test" 
>>> dbconn = sql.DriverManager.getConnection(url, user, password) 
>>> type = sql.ResultSet.TYPE_SCROLL_SENSITIVE 
>>> concurrency = sql.ResultSet.CONCUR_UPDATABLE 
>>> stmt = dbconn.createStatement(type, concurrency) 
>>> # 更新表以包含主键 
>>> query = "ALTER TABLE random ADD pkey INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY" 
>>> stmt.execute(query) 
0 
>>> # 获取可更新的结果集 
>>> rs = stmt.executeQuery("select * from random") 
>>> rs.concurrency # 1008表示可更新 
1008 
>>> # 插入一行 
>>> rs.moveToInsertRow() 
>>> rs.updateInt('number', 7) 
>>> rs.updateString('letter', 'g') 
>>> rs.updateInt('pkey', 22) 
>>> rs.insertRow() # 将其插入数据库 
>>> rs.moveToCurrentRow() 
>>> rs.relative(5) # 滚动5行 
1 
>>> print rs.getString('letter'), rs.getInt('number') 
h 8 
>>> rs.updateInt('number', 3) 
>>> rs.updateString('letter', 'c') 
>>> rs.updateRow() # 将更新保存到数据库 
>>> stmt.close() 
>>> dbconn.close() 
6.7 预编译语句

预编译频繁使用的SQL语句通常有助于提高效率。 java.sql.PreparedStatement 允许创建预编译语句。

以下是创建预编译语句的示例:

>>> import java 
>>> from java import sql 
>>> java.lang.Class.forName("org.postgresql.Driver") 
<jclass org.gjt.mm.mysql.Driver at 1876475> 
>>> dbconn = sql.DriverManager.getConnection(url, user, password) 
>>> query = "INSERT INTO random (letter, number) VALUES (?, ?)" 
>>> preppedStmt = dbconn.prepareStatement(query) 
>>> preppedStmt.clearParameters() 
>>> preppedStmt.setString(1, "f") 
>>> preppedStmt.setInt(2, 6) 
>>> preppedStmt.executeUpdate() 
1 
>>> preppedStmt.close() 
>>> dbconn.close() 
6.8 事务

事务是一组数据库操作,要么全部成功完成,要么全部撤销(回滚)。以下是使用PostgreSQL进行事务操作的示例:

>>> import java 
>>> from java import sql 
>>> java.lang.Class.forName("org.postgresql.Driver") 
<jclass org.postgresql.Driver at 5711596> 
>>> url = "jdbc:postgresql://localhost/test" 
>>> con = sql.DriverManager.getConnection(url, "jyuser", "beans") 
>>> stmt = con.createStatement() 
>>> con.autoCommit = 0 
>>> try: 
...     # 插入一个容易查找的字符 
...     query = "INSERT INTO random (letter, number) VALUES ('.', 0)" 
...     stmt.executeUpdate(query) 
...     print "First update successful." 
...     stmt.execute("Create an exception here") 
...     con.commit() 
... except: 
...     print "Error encountered, rolling back." 
...     con.rollback() 
... 
1 
First update successful. 
Error encountered, rolling back. 
>>> rs = stmt.executeQuery("SELECT * FROM random WHERE letter='.'") 
>>> rs.next() 
0 
7. zxJDBC

使用Jython与JDBC交互有一定价值,但JDBC的许多方法特定于Java类型,与Jython的高级、多态动态类型不太相符。Python有一个数据库API(Python DB API 2.0),但CPython使用的数据库驱动程序由于底层C实现,对Jython通常无用。Brian Zimmer编写了zxJDBC来填补这一空白,它不仅实现了DB API,还对其进行了扩展。

7.1 连接数据库

使用zxJDBC包时,在调用连接函数之前,需要确保 zxJDBC.jar 和所需的JDBC驱动程序在类路径中。建立数据库连接的步骤如下:
1. 将适当的驱动程序和 zxJDBC.jar 文件添加到类路径中。
2. 向 zxJDBC.connect() 方法提供JDBC URL、用户名、密码和数据库驱动程序类名。

以下是设置类路径和连接数据库的示例:

# 对于MySQL 
set CLASSPATH=mm_mysql-2_0_4-bin.jar;\path\to\zxJDBC.jar;%CLASSPATH% 
# 对于PostgreSQL 
set CLASSPATH=\path\to\jdbc7.1-1.2.jar;\path\to\zxJDBC.jar;%CLASSPATH% 
from com.ziclix.python.sql import zxJDBC 
mysqlConn = zxJDBC.connect("jdbc:mysql://localhost/test", 
                           "jyuser", "beans", 
                           "org.gjt.mm.mysql.Driver") 
postgresqlConn = zxJDBC.connect("jdbc:postgresql://localhost/test", 
                                "jyuser", "beans", 
                                "org.postgresql.Driver") 

如果需要设置特殊参数,可作为关键字参数传递:

url = "jdbc:mysql://localhost/test" 
user = "jyuser" 
password = "beans" 
driver = "org.gjt.mm.mysql.Driver" 
mysqlConn = zxJDBC.connect(url, user, password, driver, 
                           autoReconnect="true") 

连接错误会引发 DatabaseError 异常,可使用 except 语句处理:

url = "jdbc:mysql://localhost/test" 
user = "jyuser" 
password = "beans" 
driver = "org.gjt.mm.mysql.Driver" 
try: 
    mysqlConn = zxJDBC.connect(url, user, password, driver, 
                               autoReconnect="true") 
except zxJDBC.DatabaseError: 
    pass 
    # 处理错误 

如果使用 javax.sql 包中的连接工厂或实现 javax.sql.DataSource javax.sql.ConnectionPoolDataSource 的类,可使用 zxJDBC.connectx 方法:

from com.ziclix.python.sql import zxJDBC 
userInfo = {'user':'jyuser', 'password':'beans'} 
con = zxJDBC.connectx("org.gjt.mm.mysql.MysqlDataSource", 
                      serverName='localhost', databaseName='test', 
                      port=3306, **userInfo) 

还可以使用 zxJDBC.lookup 方法通过JNDI查找获取连接。

7.2 游标

zxJDBC游标是用于与数据库中的数据进行交互的对象,它实际上是JDBC语句和结果集对象的包装器。游标分为静态和动态两种类型:
- 动态游标是惰性的,仅在需要时遍历结果集,节省内存并均匀分配处理时间。
- 静态游标会立即遍历整个结果集,会产生一定的内存开销,但可以在执行语句后很快知道行数。

以下是获取游标并执行查询的示例:

from com.ziclix.python.sql import zxJDBC 
url = "jdbc:mysql://localhost/test" 
user = "jyuser" 
password = "beans" 
driver = "org.gjt.mm.mysql.Driver" 
con = zxJDBC.connect(url, user, password, driver, 
                        autoReconnect="true") 
cursor = con.cursor() # 静态游标 
# 或者创建动态游标 
cursor = con.cursor(1) 
cursor.execute("SELECT * FROM random") 

使用游标遍历结果集的方法有 fetchone() fetchmany() fetchall()

>>> cursor.fetchone() 
(41, 'O', 1) 
>>> cursor.fetchmany() 
[(6, 'f', 2)] 
>>> cursor.fetchmany(4) 
[(49, 'W', 4), (35, 'I', 5), (43, 'Q', 6), (37, 'K', 3)] 
>>> cursor.fetchall() 
[(3, 'c', 7), (17, 'q', 8), (29, 'C', 9), (36, 'J', 10), (43, 'Q', 11), (23, 
'w', 12), (49, 'W', 13), (25, 'y', 14), (40, 'N', 15), (50, 'X', 16), (46, 
'T', 17), (51, 'Y', 18), (8, 'h', 19), (25, 'y', 20), (7, 'g', 21), (11, 'k', 
22), (1, 'a', 23)] 

查询执行后,可以使用游标对象的 description 属性查看结果集的行信息。

方法/属性 描述
description 描述查询结果中每列的信息,是一个包含名称、类型代码、显示大小、内部大小、精度、比例和可空性的七项元组。
rowcount 结果集中的行数,仅在游标为静态游标或动态游标完全遍历结果集后有效。
callproc(procedureName, [parameters]) 调用存储过程,仅适用于实现了存储过程的数据库。
close() 关闭游标。
execute(statement) 执行语句。
executemany(statement, parameterList) 使用参数列表执行语句,可以在语句中使用问号表示值,并在参数列表中提供要替换的值。
fetchone() 检索查询结果的一行。
fetchmany([size]) 如果没有提供参数,返回 arraysize 数量的行;如果提供了参数 arg ,则返回 arg 数量的结果行。
fetchall() 检索所有剩余的结果行。
nextset() 继续处理下一个结果集,仅适用于支持多个结果集的数据库。
arraysize fetchmany() 在没有参数时应返回的行数。
7.3 zxJDBC与元数据

Python DB API没有元数据规范,但zxJDBC通过一些连接和游标属性提供了一些连接元数据,这些属性与JDBC的 java.sql.DatabaseMetaData 对象中的bean属性相匹配。

zxJDBC属性 DatabaseMetaData访问器
connection.dbname getDatabaseProductName
connection.dbversion getDatabaseProductVersion
cursor.tables(catalog, schemapattern, tablepattern, types) getTables
cursor.columns(catalog, schemapattern, tablenamepattern, columnnamepattern) getColumns
cursor.foreignkeys(primarycatalog, primaryschema, pimarytable, foreigncatalog, foreignschema, foreigntable) getCrossReference
cursor.primarykeys(catalog, schema, table) getPrimaryKeys
cursor.procedures(catalog, schemapattern, procedurepattern) getProcedures
cursor.procedurecolumns(catalog, schemapattern, procedurepattern, columnpattern) getProcedureColumns
cursor.statistics(catalog, schema, table, unique, approximation) getIndexInfo

以下是提取MySQL随机数据库元数据的示例:

>>> from com.ziclix.python.sql import zxJDBC 
>>> url = "jdbc:mysql://localhost/test" 
>>> driver = "org.gjt.mm.mysql.Driver" 
>>> dbconn = zxJDBC.connect(url, "jyuser", "beans", driver) 
>>> dbconn.dbname 
'MySQL' 
>>> dbconn.dbversion 
'3.23.32' 
>>> cursor = dbconn.cursor() 
>>> cursor.primarykeys(None, "%", "random") 
>>> cursor.fetchall() 
[('', '', 'random', 'pkey', 1, 'pkey')] 
7.4 预编译语句

executemany() 游标方法类似于Java的预编译语句,允许在SQL语句中使用问号表示值,并在参数列表中提供要替换的值。

以下是示例:

>>> sql = "INSERT INTO random (letter, number) VALUES (?, ?)" 
>>> cur.executemany(sql, ('Z', 51)) 
>>> cur.execute("SELECT * from random where letter='Z'") 
>>> cur.fetchall() 
[('Z', 51, 24)] 
7.5 错误和警告

zxJDBC可能引发以下异常:
- Error :通用异常。
- DatabaseError :针对特定数据库错误引发,连接对象和所有连接方法( connect lookup connectx )可能引发此异常。
- ProgrammingError :针对编程错误(如缺少参数、错误的SQL语句)引发,游标对象和查找连接可能引发此异常。
- NotSupportedError :当某个方法未实现时引发。

以下是处理异常的示例:

>>> try: 
...     pass # 假设这里有一个会引发错误的方法 
... except zxJDBC.DatabaseError: 
...     pass # 处理DatabaseError 
... except zxJDBC.ProgrammingError: 
...     pass # 处理ProgrammingError 
... except notSupportedError: 
...     pass # 处理不支持的错误 
... except zxJDBC.Error: 
...     pass # 处理通用错误异常 

可以使用游标对象的 warnings 属性获取警告信息,如果没有警告,该属性为 None

7.6 dbexts

zxJDBC包中的 dbexts 模块在Python DB API 2.0的基础上增加了一层抽象。使用 dbexts ,可以在配置文件中指定连接信息,然后使用更高级的 dbexts 方法进行操作。

以下是一个配置文件示例:

[default] 
name=mysqltest 
[jdbc] 
name=mysqltest 
url=jdbc:mysql://localhost/test 
user=jyuser 
pwd=beans 
driver=org.gjt.mm.mysql.Driver 
[jdbc] 
name=pstest 
url=jdbc:postgresql://localhost/test 
user=jyuser 
pwd=beans 
driver=org.postgresql.Driver 

连接数据库的示例:

>>> from dbexts import dbexts 
>>> mysqlcon = dbexts("mysqltest", "dbexts.ini") 
>>> psgrscon = dbexts("pstest", "dbexts.ini") 

执行SQL语句的示例:

>>> psgrscon.raw("SELECT * from random") 
([('letter', 1, 1, None, None, None, 1), ('number', 4, 11, None, 10, 0, 1)], 
[('A', 4), ('f', 6), ('a', 1)]) 
>>> psgrscon.isql("select * from random") 
LETTER | NUMBER 
---------------
A      | 4 
f      | 6 
a      | 1 
3 rows affected 
方法 描述
__init__(dbname, cfg, resultformatter, autocommit) dbexts 构造函数,所有参数都有默认值,可选。 dbname 是在 dbexts.ini 文件中为连接指定的名称, cfg dbexts.ini 文件的位置, resultformatter 是一个可调用对象,用于显示数据, autocommit 参数默认设置为1(true),可在调用构造函数时设置为0。
isql(sql, params, bindings, maxrows) 交互式执行SQL语句,执行后立即使用 resultformatter 显示结果,类似于MySQL和pSQL客户端程序。除 sql 语句外,其他参数都有默认值。 sql 是SQL语句本身, params 是用于替换SQL语句中 ? 的值的元组, bindings 允许绑定数据处理程序, maxrows 指定返回的最大行数,0或 None 表示无限制。
raw(sql, params, bindings) 执行SQL语句并返回一个包含表头和结果的元组, params bindings 参数与 isql 方法相同。
schema(table, full, sort) 显示表的索引、外键、主键和列信息。如果 full 参数非零,结果将包括表的引用键; sort 值非零表示表的列名将被排序。
table(table) table 参数可选,不提供参数时,显示所有表的列表;提供参数时,显示指定表的列信息。
proc(proc) proc 参数可选,不提供参数时,列出所有存储过程;提供参数时,显示指定存储过程的参数。
bcp(src, table, where='(1=1)') 将指定数据库和表( src table )中的数据复制到当前实例的数据库中。
begin(self) 创建一个新的游标。
rollback(self) 回滚自创建新游标以来执行的语句。
commit(self, cursor=None, maxrows=None) 提交自创建新游标以来执行的语句。
display(self) 使用当前格式化程序显示结果。

综上所述,Jython在数据库编程方面提供了丰富的工具和方法,无论是使用DBM文件、序列化对象,还是与MySQL、PostgreSQL等数据库管理系统交互,都有相应的解决方案。同时,zxJDBC和 dbexts 模块进一步扩展了Jython在数据库操作方面的能力,使得开发者可以更方便地进行数据库编程。

Jython数据库编程全解析(续)

8. 操作流程总结与对比

为了更清晰地理解Jython数据库编程的各个环节,下面将对前面介绍的主要操作流程进行总结,并通过表格和流程图的形式进行对比。

8.1 连接数据库操作对比
数据库连接方式 所需步骤 示例代码
JDBC 1. 将适当的驱动程序添加到类路径中;2. 将驱动程序注册到JDBC DriverManager;3. 向 java.sql.DriverManager.getConnection 方法提供JDBC URL、用户名和密码。 ```python
from java.sql import DriverManager
url = “jdbc:mysql://localhost/test”
user, password = “jyuser”, “beans”
try:
java.lang.Class.forName(“org.gjt.mm.mysql.Driver”)
conn = DriverManager.getConnection(url, user, password)
except java.lang.ClassNotFoundException:
print “No appropriate database driver found”
raise SystemExit
```
zxJDBC 1. 将适当的驱动程序和 zxJDBC.jar 文件添加到类路径中;2. 向 zxJDBC.connect() 方法提供JDBC URL、用户名、密码和数据库驱动程序类名。 ```python
from com.ziclix.python.sql import zxJDBC
url = “jdbc:mysql://localhost/test”
user = “jyuser”
password = “beans”
driver = “org.gjt.mm.mysql.Driver”
try:
conn = zxJDBC.connect(url, user, password, driver)
except zxJDBC.DatabaseError:
pass
# 处理错误
```
8.2 操作流程流程图
graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{选择连接方式}:::decision
    B -->|JDBC| C(添加驱动到类路径):::process
    B -->|zxJDBC| D(添加驱动和zxJDBC.jar到类路径):::process
    C --> E(注册驱动到DriverManager):::process
    D --> F(调用zxJDBC.connect方法):::process
    E --> G(获取数据库连接):::process
    F --> G
    G --> H(执行SQL操作):::process
    H --> I{操作类型}:::decision
    I -->|查询| J(获取结果集):::process
    I -->|更新| K(获取受影响行数):::process
    J --> L(处理结果集):::process
    K --> M(确认操作结果):::process
    L --> N(关闭连接):::process
    M --> N
    N --> O([结束]):::startend
9. 实际应用案例分析

为了更好地理解Jython数据库编程的实际应用,下面将通过一个简单的案例来展示如何使用Jython和数据库进行交互。

9.1 案例需求

假设我们需要开发一个简单的库存管理系统,该系统可以记录商品的名称、价格和库存数量,并支持添加商品、查询商品信息和更新库存数量的功能。我们将使用MySQL数据库来存储商品信息。

9.2 实现步骤
  1. 创建数据库和表
    首先,我们需要在MySQL中创建一个名为 inventory 的数据库,并在该数据库中创建一个名为 products 的表,用于存储商品信息。
CREATE DATABASE inventory;
USE inventory;
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    quantity INT NOT NULL
);
  1. 使用Jython连接数据库并实现功能
    以下是使用Jython和JDBC实现库存管理系统的代码示例:
import java
from java.sql import DriverManager

# 注册驱动程序
java.lang.Class.forName("org.gjt.mm.mysql.Driver")

# 连接数据库
url = "jdbc:mysql://localhost/inventory"
user, password = "jyuser", "beans"
conn = DriverManager.getConnection(url, user, password)

# 添加商品
def add_product(name, price, quantity):
    stmt = conn.createStatement()
    query = "INSERT INTO products (name, price, quantity) VALUES ('%s', %.2f, %d)" % (name, price, quantity)
    stmt.executeUpdate(query)
    stmt.close()

# 查询商品信息
def query_products():
    stmt = conn.createStatement()
    rs = stmt.executeQuery("SELECT * FROM products")
    while rs.next():
        print "ID: %d, Name: %s, Price: %.2f, Quantity: %d" % (rs.getInt('id'), rs.getString('name'), rs.getDouble('price'), rs.getInt('quantity'))
    rs.close()
    stmt.close()

# 更新库存数量
def update_quantity(product_id, new_quantity):
    stmt = conn.createStatement()
    query = "UPDATE products SET quantity = %d WHERE id = %d" % (new_quantity, product_id)
    stmt.executeUpdate(query)
    stmt.close()

# 示例操作
add_product("Widget", 112.95, 1000)
query_products()
update_quantity(1, 999)
query_products()

# 关闭连接
conn.close()

在上述代码中,我们定义了三个函数 add_product query_products update_quantity ,分别用于添加商品、查询商品信息和更新库存数量。通过调用这些函数,我们可以实现库存管理系统的基本功能。

10. 总结与建议

通过前面的介绍,我们可以看到Jython在数据库编程方面具有丰富的功能和灵活的应用方式。无论是使用DBM文件进行简单的数据存储,还是通过JDBC和zxJDBC与MySQL、PostgreSQL等数据库管理系统进行交互,Jython都能提供有效的解决方案。

10.1 优点总结
  • 与Java集成 :Jython可以无缝集成Java的数据库连接和操作功能,利用Java的强大性能和丰富的数据库驱动程序。
  • 动态类型和高级特性 :Jython的动态类型和高级特性使得数据库编程更加灵活和高效,减少了代码的复杂性。
  • 丰富的工具和模块 :Jython提供了多种工具和模块,如 marshal pickle shelve 等,方便进行对象序列化和持久化存储。
10.2 注意事项和建议
  • 驱动程序管理 :在使用JDBC或zxJDBC时,需要确保正确添加和管理数据库驱动程序,避免出现驱动找不到或版本不兼容的问题。
  • 异常处理 :数据库操作可能会出现各种异常,如连接错误、SQL语法错误等,需要在代码中进行适当的异常处理,以保证程序的稳定性。
  • 性能优化 :对于频繁使用的SQL语句,可以考虑使用预编译语句来提高性能;同时,合理使用游标和结果集,避免不必要的内存开销。

总之,Jython是一个强大的数据库编程工具,通过合理使用其提供的功能和模块,开发者可以轻松地实现各种数据库应用。希望本文的介绍能够帮助你更好地掌握Jython数据库编程的技巧和方法。

混合动力汽车(HEV)模型的Simscape模型(Matlab代码、Simulink仿真实现)内容概要:本文档介绍了一个混合动力汽车(HEV)的Simscape模型,该模型通过Matlab代码和Simulink仿真工具实现,旨在对混合动力汽车的动力系统进行建模与仿真分析。模型涵盖了发动机、电机、电池、传动系统等关键部件,能够模拟车辆在不同工况下的能量流动与控制策略,适用于动力系统设计、能耗优化及控制算法验证等研究方向。文档还提及该资源属于一个涵盖多个科研领域的MATLAB仿真资源包,涉及电力系统、机器学习、路径规划、信号处理等多个技术方向,配套提供网盘下载链接,便于用户获取完整资源。; 适合人群:具备Matlab/Simulink使用基础的高校研究生、科研人员及从事新能源汽车系统仿真的工程技术人员。; 使用场景及目标:①开展混合动力汽车能量管理策略的研究与仿真验证;②学习基于Simscape的物理系统建模方法;③作为教学案例用于车辆工程或自动化相关课程的实践环节;④与其他优化算法(如智能优化、强化学习)结合,实现控制策略的优化设计。; 阅读建议:建议使用者先熟悉Matlab/Simulink及Simscape基础操作,结合文档中的模型结构逐步理解各模块功能,可在此基础上修改参数或替换控制算法以满足具体研究需求,同时推荐访问提供的网盘链接获取完整代码与示例文件以便深入学习与调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值