80、Jython数据库编程全解析

Jython数据库编程全解析

在当今的数据驱动世界中,数据库编程是软件开发的核心部分。Jython作为一种强大的编程语言,为数据库操作提供了丰富的工具和方法。本文将深入探讨Jython在数据库编程中的应用,包括DBM文件、序列化、数据库管理系统、JDBC以及zxJDBC等方面。

1. DBM文件

DBM文件是一种哈希数据库文件,其行为类似于字典对象。不过,当前的实现存在一定限制,并非所有字典方法都能使用,且所有的键和值都必须是字符串。目前,Jython仅捆绑了一个DBM工具——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 。不过,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 方法则用于将对象序列化到字符串和从字符串中恢复对象。

以下是使用 marshal 模块处理列表对象的交互式示例:

>>> 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 模块定义了四个函数,如下表所示:
| 方法 | 描述 |
| — | — |
| Dump(object, file[, bin]) | 将内置数据对象序列化到先前打开的文件对象。如果对象无法序列化,将引发 PicklingError 异常。可以使用文本或二进制格式,第三个参数为零或缺失表示文本格式,非零表示二进制格式。 |
| Load(file) | 从先前打开的文件对象中读取并反序列化数据。如果腌制数据是以二进制模式写入的,提供给 load 的文件对象也应处于二进制模式。 |
| dumps(object[, bin]) | 将对象序列化为字符串对象,而不是文件。如果对象无法序列化,将引发 PicklingError 异常。第二个参数为零或缺失表示文本格式,非零表示二进制格式。 |
| loads(string) | 将字符串反序列化为数据对象。 |

以下是使用 pickle 存储 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() 

恢复该实例时,需要确保类存在于反序列化的命名空间中:

>>> 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 对象进行序列化和反序列化。

  • Shelves :Jython的 shelve 模块将DBM目录的便利性与 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") 
  • 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"; 
// 进行序列化,创建一个输出流(这里是FileOutputStream),然后创建一个ObjectOutputStream 
FileOutputStream oStream = new FileOutputStream(file); 
ObjectOutputStream objWriter = new ObjectOutputStream(oStream); 
// 写入一个简单的Jython对象 
objWriter.writeObject(new PyString("some string")); 
// 清理 
objWriter.flush(); 
oStream.close(); 

反序列化对象时,可以使用 ObjectInputStream PythonObjectInputStream

// 导入所需类 
import java.io.*; 
import org.python.core.*; 
import org.python.util.PythonObjectInputStream; 
// 标识资源 
String file = "someFileName"; 
// 打开一个输入流(这里是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(); 
3. 数据库管理系统

Jython可以与任何具有Java驱动程序的数据库进行交互,但本文重点介绍MySQL和PostgreSQL数据库管理系统(DBMS)。这两个数据库非常流行、免费、稳定,并且有大量的文档可供参考。

  • MySQL :MySQL是一个SQL数据库服务器,可在大多数类UNIX平台和Windows上运行。要运行本文中的MySQL特定示例,需要下载并安装MySQL及其相关的JDBC驱动程序。

安装MySQL后,需要添加一个用户和一个数据库。以下是创建测试数据库和添加用户的步骤:
1. 创建测试数据库

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) 

可以使用 SHOW DATABASES 语句确认数据库是否存在:

mysql> SHOW DATABASES\g 
+----------+ 
| Database | 
+----------+ 
| mysql    | 
| test     | 
+----------+ 
2 rows in set (0.00 sec)mysql> 
  1. 添加用户
mysql> USE test; 
mysql> GRANT ALL ON test TO jyuser@localhost IDENTIFIED BY "beans"; 
Query OK, 0 rows affected (0.05 sec) 
  • PostgreSQL :PostgreSQL是一个功能强大的对象关系数据库管理系统(ORDBMS),可在大多数类UNIX平台和Windows 2000/NT上运行。

安装PostgreSQL后,需要初始化一个数据库集群,并创建一个用户和一个测试数据库。以下是具体步骤:
1. 初始化数据库集群

initdb -D /usr/local/pgsql/data 
  1. 配置 pg_hba.conf 文件 :该文件控制哪些主机可以连接到数据库。如果从本地机器连接,主机条目可能如下所示:
host test 127.0.0.1 255.255.255.255 crypt 

认证类型可以是 Trust Password Crypt Ident Krb4 Krb5 Reject

  1. 创建用户
[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 
  1. 创建测试数据库
[shell prompt]$ createdb -U jyuser test 
CREATE DATABASE 
  1. 启动服务器
postmaster -i -D /usr/local/pgsql/data & 
4. JDBC

Java使用JDBC和 java.sql 包与SQL数据库进行交互,Jython也可以使用这些工具。使用JDBC API和 java.sql 包与SQL数据库交互时,只需为所使用的数据库提供适当的JDBC驱动程序。

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

连接本地测试数据库的URL示例如下:

jdbc:mysql://localhost:3306/test 
jdbc:postgresql://localhost:5432/test 

由于默认主机是 localhost ,默认端口分别为3306(MySQL)和5432(PostgreSQL),因此可以简化为:

jdbc:mysql:///test 
jdbc:postgresql:///test 

还可以在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 |

以下是指定使用Unicode字符编码的示例URL:

jdbc:mysql://localhost/test?useUnicode=true 
jdbc:postgresql://localhost/test?useUnicode=true 

如果排除主机名,这些URL将变为:

jdbc:mysql:///test?useUnicode=true 
jdbc:postgresql:///test?useUnicode=true 
  • JDBC连接 :使用Java的数据库连接功能建立数据库连接的步骤如下:
    1. 将适当的驱动程序包含在类路径中。
    2. 向JDBC DriverManager注册驱动程序。
    3. java.sql.DriverManager.getConnection 方法提供JDBC URL、用户名和密码。

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

# 对于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 

使用 java.sql.DriverManager getConnection 方法连接数据库,该方法有三种签名:

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 

以下是连接本地测试数据库的示例语法:

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") 
  • 连接脚本 :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 
  • 连接对话框 :客户端应用程序通常使用对话框窗口获取数据库连接信息。以下是一个数据库连接对话框的示例:
# File: DBLoginDialog.py 
import sys 
import java 
from java import awt 
from java import sql 
import pawt 
class DBLoginDialog(awt.Dialog): 
    '''DBLoginDialog提示用户输入数据库信息并建立数据库登录。注册一个连接接收器作为客户端,例如:
       def connectionClient(dbconnection): 
           # 对数据库连接进行操作 
       dbl = DBLoginDialog(parentframe, message) 
       dbl.client = connectionClient''' 
    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(self, 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 

要执行该对话框,需确保适当的数据库驱动程序在类路径中,然后执行 jython DBLoginDialog.py

  • DatabaseMetaData :连接到数据库后,可以探索数据库和连接的元数据。Java连接对象的 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() 

要执行该脚本,需确保两个数据库驱动程序都在类路径中,然后运行 jython jdbcMetaData.py

  • Statements :与MySQL和PostgreSQL进行交互的主要方式是执行SQL语句。要执行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) 

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

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

>>> query = "CREATE TABLE random ( number tinyint, letter char(1) )" 
>>> stmt.execute(query) 
0 
>>> print stmt.resultSet 
None 
>>> 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 
... 

查询数据时,使用 executeQuery 方法:

>>> rs = stmt.executeQuery("SELECT * FROM random") 
  • ResultSet :使用 executeQuery 方法返回的对象是一个 ResultSet ResultSet 对象包含许多方法,用于导航结果集并将结果作为Java原生类型检索。Jython会自动将Java原生类型转换为适当的Jython类型。

以下是查询 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 等方法。

以下是更新表并插入和更新数据的示例:

>>> 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() 
  • Prepared Statements :预编译频繁使用的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() 
  • Transactions :事务是一组必须全部成功完成的数据库操作,否则整个操作将被撤销(回滚)。以下是在PostgreSQL中创建 random 表并执行事务的示例:
>>> import java 
>>> from java import sql 
>>> java.lang.Class.forName("org.postgresql.Driver") 
<jclass org.postgresql.Driver at 5303656> 
>>> db, user, password = "test", "jyuser", "beans" 
>>> url = "jdbc:postgresql://localhost/%s" % db 
>>> dbconn = sql.DriverManager.getConnection(url, user, password) 
>>> stmt = dbconn.createStatement() 
>>> query = "CREATE TABLE random ( number int, letter char(1), pkey INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY)" 
>>> 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 
1 
... 
>>> stmt.close() 
>>> dbconn.close() 

执行事务时,需要将连接的 autoCommit 属性设置为0:

>>> 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 
5. zxJDBC

虽然使用JDBC从Jython进行数据库操作很有价值,但它是一个Java API,包含许多特定于Java类型的方法,这与Jython的高级、多态动态类型似乎相悖。

Python有一个数据库API,即Python DB API 2.0。为了填补Jython在这方面的空白,Brian Zimmer编写了zxJDBC。zxJDBC不仅实现了DB API,还对其进行了扩展。

  • 连接到数据库 :使用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查找获取连接。

  • Cursor :zxJDBC游标是实际与数据库中的数据进行交互的对象。它是JDBC Statement ResultSet 对象的包装器。静态游标和动态游标在处理结果集方面有所不同:
  • 动态游标是惰性的,仅在需要时遍历结果集,节省内存并均匀分配处理时间。
  • 静态游标会立即遍历整个结果集,会产生内存开销,但可以在执行语句后很快知道行数。

以下是获取游标并执行SQL语句的示例:

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) # 可选的布尔参数表示动态 
>>> from com.ziclix.python.sql import zxJDBC 
>>> url = "jdbc:mysql://localhost/test" 
>>> user, password, driver = "jyuser", "beans", "org.gjt.mm.mysql.Driver" 
>>> con = zxJDBC.connection(url, user, password, driver) 
>>> cursor = con.cursor() 
>>> 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 属性查看结果集中行的信息:

>>> cursor.description 
[('number', -6, 4, None, None, None, 1), ('letter', 1, 1, None, None, None, 
1), ('pkey', 4, 10, None, 10, 0, 0)] 

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

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

以下是zxJDBC游标字段和对应的 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 random 数据库中提取元数据的示例:

>>> 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.primarykeys(None, "%", "random") 
>>> cursor.fetchall() 
[('', '', 'random', 'pkey', 1, 'pkey')] 
>>> cursor.tables(None, None, '%', None) 
>>> cursor.fetchall() 
[('', '', 'random', 'TABLE', '')] 
>>> cursor.primarykeys('test', '%', 'random') 
>>> cursor.fetchall() 
[('test', '', 'random', 'pkey', 1, 'pkey')] 
>>> cursor.statistics('test', '', 'random', 0, 1) 
>>> cursor.fetchall() 
[('test', '', 'random', 'false', '', 'PRIMARY', '3', 1, 'pkey', 'A', '23', 
None, '')] 
  • Prepared Statements :游标对象的 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)] 
  • Errors and Warnings :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 属性获取警告信息。

  • dbexts :zxJDBC包的另一个扩展是 dbexts 。它是一个Python模块,在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)]) 
>>> 
>>> # 执行交互式SQL 
>>> psgrscon.isql("select * from random") 
LETTER | NUMBER 
---------------
A      | 4 
f      | 6 
a      | 1 
3 rows affected 
>>> 
>>> # 显示模式(适用于PostgreSQL,不适用于MySQL) 
>>> psgrscon.schema("random") 
Table 
random 
Primary Keys 
Imported (Foreign) Keys 
Columns 
letter                 bpchar(1), nullable 
number                 int4(4), nullable 
Indices 
>>> 
>>> # 显示MySQL 'test'数据库中的表 
>>> mysqlcon.table() 
TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS 
-----------------------------------------------------------
          |             | random     | TABLE      | 
1 row affected 

dbexts

的主要方法如下表所示:
| 方法 | 描述 |
| — | — |
| __init__(dbname, cfg, resultformatter, autocommit) | dbexts 构造函数。所有参数都有默认值,因此都是可选的。 dbname 是在 dbexts.ini 文件中为连接指定的名称。 cfg dbexts.ini 文件的位置(如果它不在 dbexts.py 文件所在的目录中)。 resultformatter 是一个可调用对象,接受一行列表作为一个参数,并可选择接受一个表头列表。调用 resultformatter 对象来显示数据。 autocommit 参数默认设置为1(或true),但在调用构造函数时可以设置为0。 |
| isql(sql, params, bindings, maxrows) | 交互式执行SQL语句。交互式意味着在执行语句后,结果会立即使用 resultformatter 显示,类似于MySQL和pSQL客户端程序。除了 sql 语句外,所有参数都有默认值。 sql 参数是SQL语句本身。 params 参数是用于替换SQL语句中 ? 的值参数元组。 bindings 参数允许绑定数据处理程序。 maxrows 指定返回的最大行数,零或 None 表示无限制。 |
| raw(sql, params, bindings) | 执行SQL语句并返回一个包含表头和结果的元组。 params bindings 参数与 isql 方法相同。 |
| schema(table, full, sort) | 显示表的索引、外键、主键和列。如果 full 参数非零,结果将包括表的引用键。非零的 sort 值表示表的列名将被排序。 |
| table(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文件操作到高级的事务处理等多个方面。以下是对各部分内容的总结以及一些实践建议。

1. DBM文件

DBM文件作为一种简单的哈希数据库文件,类似于字典对象,具有持久化的特性。在使用时,要注意当前实现的局限性,如只支持字符串键值和部分字典方法。如果对性能要求不高且需要简单的持久化存储,dumbdbm是一个不错的选择。操作步骤如下:
1. 导入 dumbdbm 模块。
2. 使用 open 方法打开或创建一个DBM文件。
3. 像操作字典一样操作DBM对象,但要注意其方法的限制。
4. 操作完成后,使用 close 方法关闭DBM对象。

import dumbdbm 
dbm = dumbdbm.open("dbmtest") 
dbm['Value 1'] = 'A' 
# 其他操作
dbm.close() 
2. 序列化

序列化是将对象转换为适合传输或存储的流的重要技术。不同的模块适用于不同类型的对象:
- marshal 模块适用于内置类型,但在Jython中使用较少。
- pickle cPickle 模块是更常用的选择, cPickle 性能更优。
- shelve 模块结合了DBM和 pickle ,可实现持久对象存储。

在使用序列化时,要注意对象的类型和序列化模块的兼容性,以及在反序列化时确保类的定义存在。操作步骤如下:
1. 选择合适的序列化模块。
2. 使用相应的方法进行序列化和反序列化操作。
3. 对于 pickle cPickle ,可根据需要使用 Pickler Unpickler 对象。

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() 

# 反序列化
f = open("1123", "rb") 
widget = cPickle.load(f) 
f.close() 
3. 数据库管理系统

MySQL和PostgreSQL是两种常用的数据库管理系统,各有特点:
- MySQL速度快,适用于对性能要求较高的场景。
- PostgreSQL功能丰富,提供了事务完整性、约束、规则等高级特性。

在使用这两种数据库时,要注意安装和配置的步骤,包括创建数据库、用户和表等操作。操作步骤如下:
1. 下载并安装数据库和相应的JDBC驱动程序。
2. 配置数据库,如创建数据库、用户和表。
3. 为数据库添加必要的权限。

-- MySQL创建数据库和用户
CREATE DATABASE test; 
USE test; 
GRANT ALL ON test TO jyuser@localhost IDENTIFIED BY "beans"; 

-- PostgreSQL初始化数据库集群和创建用户
initdb -D /usr/local/pgsql/data 
createuser -U postgres -d -P jyuser 
createdb -U jyuser test 
4. JDBC

JDBC是Java和Jython与数据库交互的重要接口。使用JDBC时,要注意以下几点:
- 理解JDBC URL的语法和参数。
- 将合适的驱动程序添加到类路径中,并注册驱动程序。
- 使用 DriverManager.getConnection 方法建立数据库连接。
- 处理连接过程中可能出现的异常。

操作步骤如下:
1. 配置类路径,添加JDBC驱动程序。
2. 注册驱动程序。
3. 构建JDBC URL,包含必要的参数。
4. 使用 DriverManager.getConnection 方法获取数据库连接。

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 

from java.sql import DriverManager 
url = "jdbc:mysql://localhost/test" 
user, password = "jyuser", "beans" 
dbconn = DriverManager.getConnection(url, user, password) 
5. zxJDBC

zxJDBC为Jython提供了更符合Python风格的数据库操作接口,实现了Python DB API 2.0并进行了扩展。使用zxJDBC时,要注意以下几点:
- 将 zxJDBC.jar 和相应的JDBC驱动程序添加到类路径中。
- 使用 zxJDBC.connect 方法建立数据库连接。
- 使用游标对象执行SQL语句并处理结果。
- 处理可能出现的异常。

操作步骤如下:
1. 配置类路径,添加 zxJDBC.jar 和JDBC驱动程序。
2. 使用 zxJDBC.connect 方法获取数据库连接。
3. 创建游标对象。
4. 使用游标对象执行SQL语句,并使用 fetchone fetchmany fetchall 方法获取结果。

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) 
cursor = con.cursor() 
cursor.execute("SELECT * FROM random") 
result = cursor.fetchall() 

流程图总结

下面是一个使用mermaid绘制的流程图,总结了使用Jython进行数据库编程的主要步骤:

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 -->|MySQL| C(安装MySQL和JDBC驱动):::process
    B -->|PostgreSQL| D(安装PostgreSQL和JDBC驱动):::process
    C --> E(配置数据库和用户):::process
    D --> E
    E --> F(添加驱动到类路径):::process
    F --> G(注册驱动程序):::process
    G --> H(构建JDBC URL):::process
    H --> I(建立数据库连接):::process
    I --> J(创建语句对象):::process
    J --> K{选择操作类型}:::decision
    K -->|查询| L(执行查询语句):::process
    K -->|插入/更新/删除| M(执行更新语句):::process
    L --> N(处理查询结果):::process
    M --> O(检查受影响行数):::process
    N --> P(关闭资源):::process
    O --> P
    P --> Q([结束]):::startend

结语

Jython数据库编程为开发者提供了丰富的工具和方法,无论是简单的DBM文件操作,还是复杂的数据库事务处理,都能找到合适的解决方案。通过深入理解和掌握这些技术,开发者可以更高效地进行数据库编程,满足不同项目的需求。在实践中,要根据具体情况选择合适的数据库和操作方法,并注意处理可能出现的异常,确保程序的稳定性和可靠性。希望本文能为你在Jython数据库编程的学习和实践中提供有价值的参考。

学生社团系统-学生社团“一站式”运营管理平台-学生社团管理系统-基于SSM的学生社团管理系统-springboot学生社团管理系统.zip-Java学生社团管理系统开发实战-源码 更多学生社团系统: SpringBoot+Vue学生社团“一站式”运营管理平台源码(活动管理+成员考核+经费审批) Java学生社团管理系统开发实战:SSM升级SpringBoot(招新报名+场地预约+数据看板) 基于SpringSecurity的社团管理APP(移动端签到+权限分级+消息推送) 企业级社团数字化平台解决方案(SpringBoot+Redis缓存+Elasticsearch活动搜索) 微信小程序社团服务系统开发(活动直播+社团文化墙+成员互动社区) SpringBoot社团核心源码(多角色支持+工作流引擎+API接口开放) AI赋能社团管理:智能匹配兴趣标签+活动热度预测+成员贡献度分析(附代码) 响应式社团管理平台开发(PC/移动端适配+暗黑模式+无障碍访问) 完整学生社团系统源码下载(SpringBoot3+Vue3+MySQL8+Docker部署) 高校垂直领域社团平台:百团大战系统+社团星级评定+跨校活动联盟 适用对象:本代码学习资料适用于计算机、电子信息工程、数学等专业正在做毕设的学生,需要项目实战练习的学习者,也适用于课程设计、期末大作业。 技术栈:前端是vue,后端是springboot,项目代码都经过严格调试,代码没有任何bug! 核心管理:社团注册、成员管理、权限分级 活动运营:活动发布、报名签到、场地预约 资源服务:经费申请、物资管理、文档共享 数据分析:成员活跃度、活动效果评估、社团影响力排名
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值