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 |
- MySQL:
- JDBC连接 :建立数据库连接的步骤如下:
- 将适当的驱动程序包含在类路径中。
- 向JDBC DriverManager注册驱动程序。
- 向
java.sql.DriverManager.getConnection方法提供JDBC URL、用户名和密码。
- JDBC URL :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驱动程序在类路径中。建立数据库连接的步骤如下:- 将适当的驱动程序和
zxJDBC.jar文件包含在类路径中。 - 向
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游标字段和对应的DatabaseMetaDatabean方法:
| 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进行数据库编程。在实际应用中,你可以根据具体需求选择合适的技术和工具,不断探索和实践,以实现高效、稳定的数据库应用。
超级会员免费看
2970

被折叠的 条评论
为什么被折叠?



