81、Jython数据库编程全解析

Jython数据库编程全解析

1. DBM文件

DBM文件是哈希数据库文件,其行为类似于字典对象,但存在一定限制。当前实现并未使用所有字典方法,且所有DBM键和值必须为字符串。Jython目前仅捆绑了dumbdbm这一DBM工具,它虽名称缺乏吸引力,实现简单且速度较慢,但仍是一个较为有效的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 模块有四个方法: dump dumps load 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对象的首选方法。 cPickle pickle 的高性能Java版本。 pickle 模块定义了四个函数,如下表所示:
    | 方法 | 描述 |
    | — | — |
    | 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() 

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

>>> 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] 
  • 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 。它有助于在反序列化继承自Java类的Jython对象时解析类。

序列化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(); 

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

// 导入所需类 
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与MySQL和PostgreSQL数据库管理系统(DBMS)进行交互。Jython可与任何具有Java驱动程序的数据库配合使用,但选择这两个数据库是因为它们非常流行、免费、稳定、常用且有大量文档。

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

安装MySQL后,需添加用户和数据库。示例中使用的用户为 jyuser ,密码为 beans ,数据库为 test 。启动服务器的命令如下:
- *nix: /pathToMysql/bin/safe_mysqld &
- Windows: \pathToMysql\bin\winmysqladmin.exe

test 数据库不存在,可使用MySQL客户端程序 mysql 连接到服务器,然后输入 CREATE DATABASE 命令创建数据库:

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> 

添加用户 jyuser 并授予其对 test 数据库的所有权限:

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服务器及其JDBC驱动程序,可从 http://www.postgresql.org/ 获取。

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

initdb -D /path/to/datadirectory 

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

initdb -D /usr/local/pgsql/data 

在创建的数据目录中,有一个 pg_hba.conf 文件,用于控制哪些主机可以连接到数据库。若要使用JDBC驱动程序连接数据库,需在该文件中为连接的机器添加主机条目。例如,从本地机器连接的主机条目如下:

host test 127.0.0.1 255.255.255.255 crypt 

认证类型包括:
- Trust:无需认证。
- Password:使用密码匹配用户名。
- Crypt:与“password”相同,但密码在网络传输时会加密。
- Ident:使用ident服务器进行认证。
- Krb4:使用Kerberos V4认证。
- Krb5:使用Kerberos V5认证。
- Reject:拒绝指定IP的连接。

创建用户 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 

创建 test 数据库:

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

启动服务器:

postmaster -i -D /usr/local/pgsql/data & 

需使用 -i 选项,因为这是PostgreSQL接受网络套接字连接的唯一方式。

4. JDBC

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

  • 连接数据库
    • 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]
        连接本地 test 数据库的示例URL如下:
      • MySQL: jdbc:mysql://localhost:3306/test
      • PostgreSQL: jdbc:postgresql://localhost:5432/test
        默认主机为 localhost ,默认端口分别为3306(MySQL)和5432(PostgreSQL),因此可简化为:
      • MySQL: jdbc:mysql:///test
      • PostgreSQL: 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 |
    • 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 

使用 DriverManager.getConnection 方法连接数据库的示例如下:

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的交互式解释器是理想的数据库客户端工具。可通过包装交互式控制台或使用 Java's Runtime.addShutdownHook 确保连接关闭,最佳方法似乎是将 InteractiveConsole 实例包装在所需的连接和关闭语句中。以下是数据库客户端启动脚本示例:
# 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 
  • 数据库元数据 :连接成功后,可探索数据库和连接的信息,即元数据。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() 
  • 语句 :与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) 

语句对象的 execute executeQuery executeUpdate 方法用于执行SQL语句,它们返回不同的结果。以下是创建表和插入数据的示例:

>>> 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 方法返回的对象是 ResultSet 。可使用 ResultSet 的导航方法遍历结果集,不同版本的JDBC和创建的语句类型会影响导航方法。以下是查询 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() 

在MySQL中,结果集可更新,但有一定条件限制。以下是更新表并插入、更新数据的示例:

>>> 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() 
  • 预编译语句 :预编译频繁使用的SQL语句可提高效率。使用 prepareStatement 方法创建预编译语句,示例如下:
>>> 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() 
  • 事务 :事务是一组必须全部成功完成的数据库操作,否则整个操作将被撤销(回滚)。以下是在PostgreSQL中使用事务的示例:
>>> 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 
... 
>>> stmt.close() 
>>> dbconn.close() 
>>> 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

虽然Jython可使用JDBC与数据库交互,但JDBC的Java类型特定方法与Jython的高级、多态动态类型不太匹配。Python的DB API 2.0是与数据库交互的标准API,但CPython使用的数据库驱动程序对Jython通常无用。因此,Brian Zimmer编写了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") 

还可在连接时设置特殊参数,处理连接错误时需捕获 DatabaseError 异常。此外,可使用 zxJDBC.connectx 方法通过连接工厂或 DataSource 进行连接,也可使用 zxJDBC.lookup 方法通过JNDI查找获取连接。

  • 游标 :zxJDBC游标是用于与数据库中的数据实际交互的对象,它是JDBC Statement ResultSet 对象的包装器。有静态和动态两种游标类型,动态游标是惰性的,仅在需要时迭代结果集,可节省内存并均匀分配处理时间;静态游标则会立即迭代整个结果集,会产生内存开销,但能快速知道行数。以下是获取游标并执行查询的示例:
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") 
>>> 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)] 
>>> 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 行数;若提供参数,则返回指定数量的结果行。 |
| fetchall() | 检索所有剩余的结果行。 |
| nextset() | 继续处理下一个结果集,仅适用于支持多个结果集的数据库。 |
| arraysize | fetchmany() 在无参数时应返回的行数。 |

  • zxJDBC和元数据 :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随机数据库元数据的示例:

>>> 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, '')] 
  • 预编译语句 :游标对象的 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)] 
  • 错误和警告 :zxJDBC可能引发的异常如下:
    • Error:通用异常。
    • DatabaseError:数据库特定错误,连接对象和所有连接方法( connect lookup connectx )可能引发此异常。
    • ProgrammingError:编程错误,如缺少参数、错误的SQL语句,游标对象和查找连接可能引发此异常。
    • NotSupportedError:方法未实现时引发此异常。

处理异常的示例如下:

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

还可使用游标对象的 warnings 属性获取警告信息。

  • dbexts :zxJDBC包的 dbexts 模块在Python DB API 2.0的基础上增加了一层抽象。可在配置文件中指定连接信息,然后使用 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 文件的位置; 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等功能进一步扩展了Jython在数据库编程方面的能力。

Jython数据库编程全解析

6. 总结与应用建议

在前面的内容中,我们详细探讨了Jython在数据库编程方面的多种技术和工具。下面对这些内容进行总结,并给出一些应用建议。

6.1 技术总结
  • DBM文件 :提供了一种简单的持久化存储方式,类似于字典,但有一定限制,适用于简单的数据存储场景。
  • 序列化 :通过 marshal pickle cPickle 模块,可将Jython对象转换为适合传输或存储的流, shelve 模块结合了数据库和 pickle 实现持久对象存储。
  • 数据库管理系统 :介绍了MySQL和PostgreSQL两种常见的数据库管理系统,以及它们的安装、配置和使用方法。
  • JDBC :Jython可借助Java的JDBC和 java.sql 包与SQL数据库交互,涵盖了连接数据库、执行SQL语句、处理结果集等操作。
  • zxJDBC :实现了Python的DB API 2.0,并进行了扩展,提供了更简洁的数据库操作接口,包括连接数据库、使用游标、处理元数据等功能。
6.2 应用建议
  • 简单数据存储 :如果只需要简单的键值对存储,且数据量较小,可使用DBM文件。例如,存储一些配置信息或临时数据。
import dumbdbm 
dbm = dumbdbm.open("config") 
dbm['username'] = 'user123' 
dbm['password'] = 'pass123' 
print(dbm['username']) 
dbm.close() 
  • 对象序列化 :当需要存储或传输复杂的Jython对象时,可使用 pickle cPickle 模块。例如,保存自定义类的实例。
import cPickle 
class MyClass: 
    def __init__(self, value): 
        self.value = value 

obj = MyClass(42) 
with open('myobj.pkl', 'wb') as f: 
    cPickle.dump(obj, f) 

with open('myobj.pkl', 'rb') as f: 
    loaded_obj = cPickle.load(f) 
print(loaded_obj.value) 
  • 数据库交互 :对于需要与数据库进行交互的项目,根据数据库的特点和需求选择合适的数据库管理系统。如果对性能要求较高,可选择MySQL;如果需要更高级的功能,如事务处理、对象关系映射等,可选择PostgreSQL。
# MySQL示例 
import java 
from java.sql import DriverManager 
try: 
    java.lang.Class.forName("org.gjt.mm.mysql.Driver") 
    url = "jdbc:mysql://localhost/test" 
    user = "jyuser" 
    password = "beans" 
    dbconn = DriverManager.getConnection(url, user, password) 
    stmt = dbconn.createStatement() 
    query = "SELECT * FROM users" 
    rs = stmt.executeQuery(query) 
    while rs.next(): 
        print(rs.getString('username')) 
    stmt.close() 
    dbconn.close() 
except java.lang.ClassNotFoundException: 
    print("No appropriate database driver found") 
  • 简化操作 :使用zxJDBC可简化数据库操作,提高开发效率。例如,使用游标执行查询和获取结果。
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 users") 
results = cursor.fetchall() 
for row in results: 
    print(row) 
cursor.close() 
con.close() 
7. 流程图总结

为了更清晰地展示Jython数据库编程的流程,下面给出几个关键操作的流程图。

7.1 数据库连接流程
graph TD;
    A[开始] --> B[包含驱动到类路径];
    B --> C[注册驱动到DriverManager];
    C --> D[提供URL、用户名和密码];
    D --> E[调用getConnection方法];
    E --> F{连接成功?};
    F -- 是 --> G[获取数据库连接];
    F -- 否 --> H[处理连接错误];
    G --> I[结束];
    H --> I;
7.2 数据查询流程
graph TD;
    A[开始] --> B[获取数据库连接];
    B --> C[创建语句对象];
    C --> D[执行查询语句];
    D --> E[获取结果集];
    E --> F{有结果?};
    F -- 是 --> G[遍历结果集];
    F -- 否 --> H[无结果处理];
    G --> I[处理结果];
    I --> J[关闭结果集];
    J --> K[关闭语句对象];
    K --> L[关闭数据库连接];
    H --> L;
    L --> M[结束];
8. 常见问题与解决方案

在使用Jython进行数据库编程时,可能会遇到一些常见问题,下面给出相应的解决方案。

8.1 驱动加载问题
  • 问题描述 :使用 Class.forName 方法加载驱动时,可能会抛出 ClassNotFoundException 异常。
  • 解决方案 :确保驱动程序的JAR文件已正确添加到类路径中。例如,对于MySQL驱动,可使用以下命令添加到类路径:
set CLASSPATH=\path\to\mm_mysql-2_0_4-bin.jar;%CLASSPATH% 
8.2 连接错误
  • 问题描述 :调用 DriverManager.getConnection 方法时,可能会抛出 SQLException 异常。
  • 解决方案 :检查URL、用户名和密码是否正确,以及数据库服务器是否正常运行。例如,确保数据库服务已启动,且URL中的数据库名称存在。
8.3 结果集处理问题
  • 问题描述 :在处理结果集时,可能会遇到空指针异常或数据类型不匹配的问题。
  • 解决方案 :在使用结果集之前,先检查是否为空。在获取数据时,确保使用正确的数据类型方法。例如:
rs = stmt.executeQuery("SELECT * FROM users") 
if rs.next(): 
    username = rs.getString('username') 
    age = rs.getInt('age') 
    print(username, age) 
9. 未来发展趋势

随着技术的不断发展,Jython在数据库编程方面也可能会有一些新的发展趋势。

9.1 更多数据库支持

未来可能会有更多的数据库驱动支持Jython,使得Jython可以与更多类型的数据库进行交互,如NoSQL数据库。

9.2 性能优化

随着Jython自身性能的提升,以及对数据库操作的优化,使用Jython进行数据库编程的性能可能会进一步提高。例如,优化序列化和反序列化的速度,以及提高数据库查询和更新的效率。

9.3 集成更多工具

可能会有更多的工具和框架与Jython集成,使得数据库开发更加便捷。例如,集成数据可视化工具,方便对数据库中的数据进行可视化展示。

10. 结论

Jython为数据库编程提供了丰富的功能和工具,无论是简单的数据存储还是复杂的数据库管理系统交互,都可以通过合适的模块和技术实现。通过本文的介绍,我们了解了DBM文件、序列化、MySQL和PostgreSQL数据库管理系统、JDBC和zxJDBC等技术的使用方法。同时,我们还给出了应用建议、流程图、常见问题解决方案以及未来发展趋势。希望这些内容能够帮助你更好地使用Jython进行数据库编程。在实际应用中,你可以根据具体需求选择合适的技术和工具,不断探索和实践,以实现高效、稳定的数据库应用。

内容概要:本文详细介绍了“秒杀商城”微服务架构的设计与实战过程,涵盖系统从需求分析、服务拆分、技术选型到核心功能开发、分布式事务处理、容器化部署及监控链路追踪的完整流程。重点解决了高并发场景下的超卖问题,采用Redis预减库存、消息队列削峰、数据库乐观锁等手段保障数据一致性,并通过Nacos实现服务注册发现与配置管理,利用Seata处理跨服务分布式事务,结合RabbitMQ实现异步下单,提升系统吞吐能力。同时,项目支持Docker Compose快速部署和Kubernetes生产级编排,集成Sleuth+Zipkin链路追踪与Prometheus+Grafana监控体系,构建可观测性强的微服务系统。; 适合人群:具备Java基础和Spring Boot开发经验,熟悉微服务基本概念的中高级研发人员,尤其是希望深入理解高并发系统设计、分布式事务、服务治理等核心技术的开发者;适合工作2-5年、有志于转型微服务或提升架构能力的工程师; 使用场景及目标:①学习如何基于Spring Cloud Alibaba构建完整的微服务项目;②掌握秒杀场景下高并发、超卖控制、异步化、削峰填谷等关键技术方案;③实践分布式事务(Seata)、服务熔断降级、链路追踪、统一配置中心等企业级中间件的应用;④完成从本地开发到容器化部署的流程落地; 阅读建议:建议按照文档提供的七个阶段循序渐进地动手实践,重点关注秒杀流程设计、服务间通信机制、分布式事务实现和系统性能优化部分,结合代码调试与监控工具深入理解各组件协作原理,真正掌握高并发微服务系统的构建能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值