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>
- 添加用户 :
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
-
配置
pg_hba.conf文件 :该文件控制哪些主机可以连接到数据库。如果从本地机器连接,主机条目可能如下所示:
host test 127.0.0.1 255.255.255.255 crypt
认证类型可以是
Trust
、
Password
、
Crypt
、
Ident
、
Krb4
、
Krb5
或
Reject
。
- 创建用户 :
[shell prompt]$ createuser -U postgres -d -P jyuser
Enter password for user "jyuser":
Enter it again:
Shall the new user be allowed to create more new users? (y/n) n
CREATE USER
- 创建测试数据库 :
[shell prompt]$ createdb -U jyuser test
CREATE DATABASE
- 启动服务器 :
postmaster -i -D /usr/local/pgsql/data &
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的数据库连接功能建立数据库连接的步骤如下:
- 将适当的驱动程序包含在类路径中。
- 向JDBC DriverManager注册驱动程序。
-
向
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驱动程序在类路径中。建立数据库连接的步骤如下:-
将适当的驱动程序和
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")
还可以在连接函数中使用关键字参数指定驱动程序所需的特殊参数:
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数据库编程的学习和实践中提供有价值的参考。
超级会员免费看
867

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



