﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-&lt;font size="5"&gt;Web2.0学习园地&lt;/font&gt;-文章分类-PostgreSql</title><link>http://www.blogjava.net/CrazyJava/category/26050.html</link><description>&lt;marquee behavior="alternate" scrolldelay=3 scrollamount=3&gt;
&lt;font face="楷体_GB2312" color="#FFFF00" size="4"&gt;网络无所不在,应用无所不在,服务无所不在!&lt;/font&gt;
&lt;/marquee&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 21 Sep 2007 05:32:04 GMT</lastBuildDate><pubDate>Fri, 21 Sep 2007 05:32:04 GMT</pubDate><ttl>60</ttl><item><title>pgsql function 系列之一：返回结果集</title><link>http://www.blogjava.net/CrazyJava/articles/147058.html</link><dc:creator>CrazyJava</dc:creator><author>CrazyJava</author><pubDate>Fri, 21 Sep 2007 04:43:00 GMT</pubDate><guid>http://www.blogjava.net/CrazyJava/articles/147058.html</guid><wfw:comment>http://www.blogjava.net/CrazyJava/comments/147058.html</wfw:comment><comments>http://www.blogjava.net/CrazyJava/articles/147058.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/CrazyJava/comments/commentRss/147058.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/CrazyJava/services/trackbacks/147058.html</trackback:ping><description><![CDATA[pgsql function 系列之一：返回结果集<br />
--------------------------------------------------------------------------------<br />
&nbsp;我们在编写postgresql数据库的函数（或称为存储过程）时，时常会遇到需要返回一个结果集的情况，如何返回一个结果集，返回一个结果集有多少种方式，以及如何选择一个合适的方式返回结果集，这是一个需要仔细考虑的问题。本文仅简单的罗列出各种返回结果集的方式并试图分析他们的特点，而采用何种方式则留给大家自己去判断。 <br />
阅读本文需要一定的postgresql数据库端编程知识，如果你缺乏这方面的知识，请先翻阅postgresql文档。 <br />
&nbsp;<br />
----------------------------------------------------分割线------------------------------------------------------- <br />
&nbsp;<br />
第一种选择：声明setof 某表/某视图 返回类型 <br />
这是postgresql官方推荐的返回结果集的形式，当你查阅postgresql官方文档的时候，你会看到的就是这种形式。如果采用这种形式，你的function代码看起来会像这样：CREATE OR REPLACE FUNCTION function1 () RETURNS setof table1 AS <br />
$body$ <br />
DECLARE <br />
result record; <br />
BEGIN <br />
for result in select * from table1 loop <br />
return next result; <br />
end loop; <br />
return; <br />
END; <br />
$body$ <br />
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; <br />
&nbsp;<br />
这是使用pl/pgsql语言的风格，你甚至可以使用sql语言让代码看起来更简单： <br />
&nbsp;<br />
CREATE OR REPLACE FUNCTION function1 () RETURNS SETOF table1 AS <br />
$body$ <br />
SELECT * from table1; <br />
$body$ <br />
LANGUAGE 'sql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; <br />
&nbsp;<br />
以下是分析： <br />
首先我们说优点，第一、这是官方推荐的；第二、当使用pl/pgsql语言的时候，我们可以在循环中加上判断语句，仅返回我们需要的行；第三、在jdbc调用中，我们可以像查询普通的表一样使用这个function，例如："select * from function1()"。 <br />
其次我们说缺点，第一、当使用pl/pgsql语言的时候，即使我们需要返回所有行，仍然要进行循环导致不必要的开销。当然你可以使用sql语言避免这个问题，但显然sql语言的控制能力太弱以至于我们无法使用它实现哪怕稍微复杂一点的逻辑。第二、返回的字段必须是在function定义时就确定了的，这意味着我们无法动态的返回我们想要返回的字段。 <br />
&nbsp;<br />
------------------------------------------------继续分割线------------------------------------------------- <br />
&nbsp;<br />
第二种选择：声明setof record返回类型 <br />
总的说起来这种方式和上一种没什么不同，它唯一的用处在于&#8220;一定程度上&#8221;解决了动态返回结果集字段的问题。之所以说&#8220;一定程度&#8221;是因为使用setof record返回类型将会导致极度恶心的jdbc端编码——你不得不在sql语句后面显式的说明所选字段的名称和类型，并且你会发现你无法使用jdbc call的方式调用function（关于jdbc call的部分在另外的篇幅描述）。具体的来说，如果你返回一个结果集字段按照：a bigint，b varchar，c timestamp排列，则你的jdbc端编码看起来会像这样： <br />
&nbsp;<br />
select * from function1() as (a bigint,b varchar,c timestamp); <br />
&nbsp;<br />
问题在于，如果你不知道function将要返回的字段类型是什么，则你根本无法在jdbc端调用该function！！！ <br />
&nbsp;<br />
----------------------------------------------还是分割线------------------------------------------------- <br />
&nbsp;<br />
第三种选择：声明refcursor返回类型 <br />
事情到这里将揭过新的一页，这里我们放弃使用setof &#215;&#215;&#215; ，而使用一个全新的返回类型——refcursor（游标）。关于什么是游标本文不再累述，请自己翻阅相关文档，这里仅描述如何使用。 <br />
首先，要使用游标，你的function编码看起来会像这样： <br />
CREATE OR REPLACE FUNCTION function1 () RETURNS refcursor AS <br />
$body$ <br />
DECLARE <br />
result refcursor; <br />
BEGIN <br />
open result for select * from table1,table2; --你可以任意选择你想要返回的表和字段 <br />
return result; <br />
END; <br />
$body$ <br />
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; <br />
&nbsp;<br />
你的jdbc端编码推荐使用call方式，看起来会像这样： <br />
&nbsp;<br />
Connection conn = ConnectionPool.getConn(); <br />
CallableStatement proc = conn.prepareCall("{ ? = call function1() }"); <br />
proc.execute(); <br />
ResultSet rs = (ResultSet) proc.getObject(1); <br />
&nbsp;<br />
如此我们就获得了一个结果集，并且该结果集所包含的字段可以是任意你想要的字段，并且jdbc端编码也很简洁。 <br />
&nbsp;<br />
然而这种方式的缺点依然很明显：我们不能对要返回的结果集做进一步的筛选（参考第一种选择的优点二）。 <br />
----------------------------------------------最后分割线------------------------------------------------ <br />
&nbsp;<br />
总的来说，上述三种方法各有特点，我们在实际应用中应该秉着&#8220;合适的就是最好的&#8221;的原则来看待问题。 <br />
<br />
<br />
<img src ="http://www.blogjava.net/CrazyJava/aggbug/147058.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/CrazyJava/" target="_blank">CrazyJava</a> 2007-09-21 12:43 <a href="http://www.blogjava.net/CrazyJava/articles/147058.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>pgsql function 系列之二：使用数组</title><link>http://www.blogjava.net/CrazyJava/articles/147059.html</link><dc:creator>CrazyJava</dc:creator><author>CrazyJava</author><pubDate>Fri, 21 Sep 2007 04:43:00 GMT</pubDate><guid>http://www.blogjava.net/CrazyJava/articles/147059.html</guid><wfw:comment>http://www.blogjava.net/CrazyJava/comments/147059.html</wfw:comment><comments>http://www.blogjava.net/CrazyJava/articles/147059.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/CrazyJava/comments/commentRss/147059.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/CrazyJava/services/trackbacks/147059.html</trackback:ping><description><![CDATA[pgsql function 系列之二：使用数组<br />
--------------------------------------------------------------------------------<br />
我们在编码的过程中，经常会遇到需要传递一组数据的情况。在java中，我们有很多选择，除了基本的数组之外，还有很多类型如：List、Set、Map等等，但当我们需要在jdbc端和数据库端传递一组数据的时候，我们能使用的只有数组。总的来说，虽然postgresql数据库本身提供的数组功能是相当丰富和强大的，但其jdbc实现对传递数组的支持是比较薄弱的，首先它没有提供一个java.sql.Array的实现类以供我们在调用CallableStatement.setArray(int,Array)的时候使用，其次当使用RowSet遍历包含数组类型的字段的结果集时会抛出异常。经过一段时间的摸索，我们逐渐找到了上述两个问题原因所在和解决方法，下面我们详细讨论。 <br />
&nbsp;<br />
第一个问题的解决思路很简单——既然postgresql的jdbc包没有提供Array的实现类，我们就自己实现一个。问题的关键是我们需要实现什么，怎么实现。要回答这个问题没有其他的方法，只能去找jdbc的源码，从源码中找答案。所幸的是postgresql的jdbc源码是开放的，经过一番查找，我们注意到这个方法，秘密全在里面： <br />
&nbsp;<br />
package org.postgresql.jdbc2; <br />
&nbsp;<br />
public abstract class AbstractJdbc2Statement implements BaseStatement <br />
{ <br />
public void setArray(int i, java.sql.Array x) throws SQLException <br />
{ <br />
checkClosed(); <br />
&nbsp;<br />
if (null == x) <br />
{ <br />
setNull(i, Types.ARRAY); <br />
return; <br />
} <br />
&nbsp;<br />
// This only works for Array implementations that return a valid array 从这里可以知道我们 <br />
// literal from Array.toString(), such as the implementation we return 需要重载toString()方法 <br />
// from ResultSet.getArray(). Eventually we need a proper implementation 将数组序列化为指定的 <br />
// here that works for any Array implementation. 格式。 <br />
&nbsp;<br />
// Use a typename that is "_" plus the base type; this matches how the 从这里可以知道我们 <br />
// backend looks for array types. 需要实现getBaseTypeName() <br />
String typename = "_" + x.getBaseTypeName(); 方法,并且返回一个与数据库数组 <br />
int oid = connection.getPGType(typename); 元素类型对应的名称字符串 <br />
if (oid == Oid.UNSPECIFIED) <br />
throw new PSQLException(GT.tr("Unknown type {0}.", typename), PSQLState.INVALID_PARAMETER_TYPE); <br />
&nbsp;<br />
setString(i, x.toString(), oid); <br />
} <br />
} <br />
&nbsp;<br />
接下来我们需要搞清楚两个问题： <br />
1、数组的序列化格式是什么？ <br />
2、对应每个数据库数据类型的准确的名称是什么？ <br />
&nbsp;<br />
to be continue...<br />
<img src ="http://www.blogjava.net/CrazyJava/aggbug/147059.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/CrazyJava/" target="_blank">CrazyJava</a> 2007-09-21 12:43 <a href="http://www.blogjava.net/CrazyJava/articles/147059.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>