elathen | 04 九月, 2008 16:00
随着不同应用模块间的消息交互和通信成为一个关键的功能,并且变得越来越重要。Oracle引入了一种强大的队列机制,通过它程序间可以实现信息的交互,oracle把它称作为AQ - Advanced Queuing. 使用Oracle AQ,我们不需要安装额外的中间件,它是Oracle数据库的一个功能组件,只要你安装了Oracle 数据库就可以使用AQ了。接下来分两部分来介绍AQ的使用,使用之前我们要创建QUEUE.
我们创建一个自己的AQ的管理角色 "my_aq_adm_role" 和管理用户"aqadm",再把Oracle AQ 管理角色 "aq_adminstrator_role" 授权给"my_aq_adm_role".
CREATE ROLE my_aq_adm_role;
GRANT aq_adminsistator_role TO my_aq_adm_role
创建一个用户的角色 "my_aq_user_role" 和 普通用户"aquser" ,再把Oracle AQ的用户角色"aq_user_role"和一些基本操作需要的系统权限授权给 "my_aq_adm_role"
CREATE ROLE my_aq_user_role;
GRANT CREATE session, aq_user_role TO my_aq_user_role;
EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(
privilege => 'ENQUEUE_ANY',
grantee => 'my_aq_user_role',
admin_option => FALSE);
EXEC DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(
privilege => 'DEQUEUE_ANY',
grantee => 'my_aq_user_role',
admin_option = 'FALSE');
现在我们创建 AQ管理用户
CREATE USER aqadm IDENTIFIED BY aqadm
DEFAULT TABLESPACE elathen
TEMPORARY TABLESPACE temp;
GRANT my_aq_adm_role TO aqadm;
GRANT connect, resource TO aqadm; ----注意,resource角色一定要授给user,如果resource角色授权给
my_aq_adm_role, user将会失去 unlimited tablespace 权限
接着为我们下面example创建普通用户
CREATE USER aquser IDENTIFIED BY aquser
DEFAULET TABLESPACE elathen
TEMPORARY TABLESPACE temp;
GRANT my_aq_user_role TO aquser;
我们将在我们第一个queue中使用object type而不是NUMBER or VARCHAR2作为payload,这使我们更贴近实际应用。(payload是任何消息都使用的一种数据类型和结构).
上面做的工作都需要在DBA的权限下做,现在我们切换到AQ管理员
CONNECT aqadm/aqadm
CREATE TYPE queue_message_type AS OBJECT(
no NUBER,
title VARCHAR2(30),
text VARCHAR2(2000));
/
GRANT EXECUTE ON queue_message_type TO my_aq_user_role;
我们再创建一个叫"message_queue"的queue以及相应的queue table "queue_message_table",然后启动queue,这样我们就可以使用了。
1.PL/SQL中使用AQ和java使用oracle本地AQ
点对点模型(The Point-to-point Model)
在简单的系统中,我们可以认为两个系统一起使用一个或多个Queue。这种方法我们称作点对点模型。把消息输入到queue中的过程称为入列(Enqueue)相反的过程称为出列(Dequeue)。一条消息一次只能被一个使用这个queue的应用系统Dequeue,当其他应用系统可以浏览这个queue。这种模式就是点对点模式(the point-point Model)

PL/SQL中使用AQ
使用aquser连接到数据库
CONNECT aquser/aquser
现在我们Enqueue一条消息.
DECLARE
queue_options | DBMS_AQ.enqueue_options_t; |
message_properties DBMS_AQ.message_properties_t;
message_id | RAW(16); |
my_message | aqadm.queue_message_type; |
BEGIN
my_message := aqadm.queue_message_type(1,
'This is a sample message',
'This message has been posted on' || to_char(SYSDATE,'DD.MM.YYYY HH24:MI:SS'));
DBMS_AQ.enqueue(queue_name => 'aqadm.message_queue',
enqueue_options => queue_options,
message_properties => message_properties,
payload => my_message,
msgid => message_id);
COMMIT;
END;
/
我们现在Dequeue刚才入列的消息,先执行
SET SERVEROUTPUT ON
然后
DECLARE
queue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
message_id RAW(2000);
my_message aqadm.queue_message_type;
BEGIN
DBMS_AQ.DEQUEUE(
queue_name => 'aqadm.message_queue',
dequeue_options => queue_options,
message_properties => message_properties,
payload => my_message,
msgid => message_id );
COMMIT;
DBMS_OUTPUT.PUT_LINE(
'Dequeued no: ' || my_message.no);
DBMS_OUTPUT.PUT_LINE(
'Dequeued title: ' || my_message.title);
DBMS_OUTPUT.PUT_LINE(
'Dequeued text: ' || my_message.text);
END;
/
上面的PL/SQL的例子比较简单和直接,任何应用和编程环境都可以这样做。然而实际项目中,可能用编程语言来处理消息会更便利和更有实用价值。接下来我们将讨论在java中使用AQ
Java 中使用Oracle Native AQ
在前面的例子里我们为队列消息创建了一个Oracle Object type "queue_message_type",在java语言中我们不能使用Oracle的数据类型,因此我们要创建一个和"queue_message_type"对应的java类。我们可以使用Oracle Jpublisher,通过它,我们可以创建一个和Oracle Object type对应的java类。(这里就不具体讨论JPublisher用法)
这里我们用JPubisher创建一个和Oracle Object type "queue_message_type"对应java class "QUEUE_MESSAGE_TYPE "
在使用Oracle Native AQ对java的interface之前,我们必须通过jdbc连接到数据库,代码如下
//loads the Oracle JDBC driver
Class.forName("oracle.jdbc.driver.OracleDriver");
NativeAQ aq = new NativeAQ();
//DB connection, HOST --- 数据库所在的机器domian id SID----数据的service name
aq.connection = DriverManager.getConnection("jdbc:oracle:thin:@HOST:1521:SID","aquser","aquesr")
//
aq.connection.setAutoCommit(false);
然后我们通过传入AQ connection来获取AQ session对象
//loads the Oracle AQ driver
Class.forName("oracle.AQ.AQOracleDriver");
aq.session = AQDriverManager.createAQSession(aq.connection);
上述工作做好后,我们可以获取我们需要的queue对象了,进行出列的操作。
AQQueue queue = aq.session.getQueue("aqadm","MESSAGE_QUEUE")
AQDequeueOption dequeueOption = new AQDequeueOption();
System.out.println("Waiting for message to dequeue……...");
AQMessage message =
((AQOracleQueue)queue).dequeue(dequeueOption,QUEUE_MESSAGE_TYPE.getFactory());
把raw payload 转换成我们消息类型
AQObjectPayload payload = message.getObjectPayload();
QUEUE_MESSAGE_TYPE messageData = (QUEUE_MESSAGE_TYPE) payload.getPayloadData();
aq.connection.commit();
System.out.println("Dequeued no: " + messageData.getNo());
System.out.println("Dequeued title: " + messageData.getTitle);
System.out.pritnln("Dequeued text: " + messageData.getText());
小结:Oracle Advanced Queuing是一个功能强大并且使用起来相对简单的产品。它提供的java包使我们不用编写太多的java代码就可以实现enqueue和dequeue操作。
elathen | 14 六月, 2007 12:57
elathen | 23 三月, 2007 22:04
elathen | 23 三月, 2007 21:51
elathen | 21 二月, 2007 22:08
elathen | 16 一月, 2007 23:01
Use a MethodInterceptor为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:
package com.lizjason.cglibproxy;
import java.lang.reflect.Method;
/**
* A simple authorization service for illustration purpose.
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public interface AuthorizationService {
/**
* Authorization check for a method call. An AuthorizationException
* will be thrown if the check fails.
*/
void authorize(Method method);
}对net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:
package com.lizjason.cglibproxy.impl;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.lizjason.cglibproxy.AuthorizationService;
/**
* A simple MethodInterceptor implementation to
* apply authorization checks for proxy method calls.
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*
*/
public class AuthorizationInterceptor implements MethodInterceptor {
private AuthorizationService authorizationService;
/**
* Create a AuthorizationInterceptor with the given
* AuthorizationService
*/
public AuthorizationInterceptor (AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
/**
* Intercept the proxy method invocations to inject authorization check.
* The original method is invoked through MethodProxy.
* @param object the proxy object
* @param method intercepted Method
* @param args arguments of the method
* @param proxy the proxy used to invoke the original method
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @return any value compatible with the signature of the proxied method.
*/
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {
if (authorizationService != null) {
//may throw an AuthorizationException if authorization failed
authorizationService.authorize(method);
}
return methodProxy.invokeSuper(object, args);
}
}
我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用
java.lang.reflect.Method对象。
Use a CallbackFilter
net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:save和load,其中方法save需要权限检查,而方法load不需要权限检查。
package com.lizjason.cglibproxy.impl;
import com.lizjason.cglibproxy.PersistenceService;
/**
* A simple implementation of PersistenceService interface
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public class PersistenceServiceImpl implements PersistenceService {
public void save(long id, String data) {
System.out.println(data + " has been saved successfully.");
}
public String load(long id) {
return "Jason Zhicheng Li";
}
}
注意到PersistenceServiceImpl类实现了PersistenceService 接口,因此没有要求要使用CGLIB创建代理。
net.sf.cglib.proxy.CallbackFilter 接口的实现如下:
package com.lizjason.cglibproxy.impl;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
/**
* An implementation of CallbackFilter for PersistenceServiceImpl
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public class PersistenceServiceCallbackFilter implements CallbackFilter {
//callback index for save method
private static final int SAVE = 0;
//callback index for load method
private static final int LOAD = 1;
/**
* Specify which callback to use for the method being invoked.
* @method the method being invoked.
* @return the callback index in the callback array for this method
*/
public int accept(Method method) {
String name = method.getName();
if ("save".equals(name)) {
return SAVE;
}
// for other methods, including the load method, use the
// second callback
return LOAD;
}
}
accept方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。
...
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);
CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
enhancer.setCallbackFilter(callbackFilter);
AuthorizationService authorizationService = ...
Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
enhancer.setCallbacks(callbacks);
...
return (PersistenceServiceImpl)enhancer.create();
在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过
net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])方法指定代理对象所实现的接口。
除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用,net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。你可以从http://www.lizjason.com/downloads/下载设置回调类型和接口的完整代码。
小结 GLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。 它底层使用字节码处理框架ASM。其原理是,生产一个要代理类的子类,子类覆盖要代理的类的所有不是final的方法。 它比使用java反射的JDK动态代理要快。通常情况下,你可以使用JDK的动态代理创建代理,当你要代理的类没有实现接口 或者为了更好的性能,CGLIB是一个好的选择。
elathen | 14 一月, 2007 23:00
net.sf.cglib.proxy.FixedValue net.sf.cglib.proxy.NoOp net.sf.cglib.proxy.LazyLoader net.sf.cglib.proxy.Dispatcher net.sf.cglib.proxy.ProxyRefDispatcher 如图3所示,代理类的所以方法经常会用到回调(callback),当是你也可以使用net.sf.cglib.proxy.CallbackFilter 有选择的对一些方法使用回调(callback),这种考虑周详的控制特性在JDK的动态代理中是没有的。在JDK代理中,对java.lang.reflect.InvocationHandler方法的调用对代理类的所以方法都有效。
除了代理类外,CGLIB通过提供一个对java.lang.reflect.Proxy的drop-in替代来实现对对接口的代理。因为这种代理能力的替代很少被用到,因此相应的APIs也很少提到。
CGLIB的代理包也对net.sf.cglib.proxy.Mixin提供支持。基本上,它允许多个对象被绑定到一个单个的大对象。在代理中对方法的调用委托到下面相应的对象中。
接下来我们看看如何使用CGLIB代理APIs创建代理。
创建一个简单的代理CGLIB代理最核心的是net.sf.cglib.proxy.Enhancer类,为了创建一个代理,最起码你要用到这个类。首先,让我们使用NoOp回调创建一个代理:
/**
* Create a proxy using NoOp callback. The target class
* must have a default zero-argument constructor.
*
* @param targetClass the super class of the proxy
* @return a new proxy for a target class instance
*/
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(NoOp.INSTANCE);
return enhancer.create();
}
返回值是target类一个实例的代理。在这个例子中,我们为net.sf.cglib.proxy.Enhancer 配置了一个单一的回调(callback)。我们可以看到很少直接创建一个简单的代理,而是创建一个net.sf.cglib.proxy.Enhancer的实例,在net.sf.cglib.proxy.Enhancer类中你可使用静态帮助方法创建一个简单的代理。一般推荐使用上面例子的方法创建代理,因为它允许你通过配置net.sf.cglib.proxy.Enhancer实例很好的控制代理的创建。
要注意的是,target类是作为产生的代理的父类传进来的。不同于JDK的动态代理,它不能在创建代理时传target对象,target对象必须被CGLIB包来创建。在这个例子中,默认的无参数构造器时用来创建target实例的。如果你想用CGLIB来创建有参数的实例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一个参数定义了参数的类型,第二个是参数的值。在参数中,基本类型应被转化成类的类型。
------------
如果要装载,请注明出处
elathen | 12 一月, 2007 22:58
大多时候,仅仅为了动态地创建代理,你仅需要使用到代理包中很少的一些API。
正如我们先前所讨论的,CGLIB包是在ASM之上的一个高级别的层。对代理那些没有实现接口的类非常有用。本质上,它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors),这比JDK动态代理方法快多了。
图2为我们演示了创建一个具体类的代理时,通常要用到的CGLIB包的APIs。net.sf.cglib.proxy.Callback接口在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。net.sf.cglib.pr用oxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;
(原文请参照http://www.ociweb.com/jnb/jnbNov2005.html)
------------
如果要装载,请注明出处
elathen | 14 六月, 2006 21:53
Winners by Category
(Click here to view this year's list of finalists.)
1. Books: Practical/General Developer Interest: Computer history, best practices, project management, developer or team efficiency, and socioeconomic, regulatory or career issues relating to software.
Jolt Winner: Prefactoring by Ken Pugh (O'Reilly)
2. Books: Technical: Specific languages, technologies, platforms, hardware, operating systems, coding styles, modeling, architecture, source-code optimization and code-level security.
Jolt Winner: Agile Web Development with Rails by Dave Thomas, David Hansson, Leon Breedt and Mike Clark (Pragmatic Bookshelf)
3. Enterprise Project Management Tools: Tools for enterprise application integration (EAI), customer relationship management (CRM), business-process orchestration or integration, middleware and database applications that streamline business processes and improve integration.
Jolt Winner: WelcomRisk 2.6 (Welcom)
4. Database Engines and Data Tools: Tools that help facilitate, maintain and manage the storage and retrieval of data and objects, including search tools for both desktop and enterprise.
Jolt Winner: Microsoft SQL Server 2005 (Microsoft)
5. Defect Tracking, Change and Configuration Management Tools: Tools that track and manage software defects and enhancement requests as well as version control and change or configuration management.
Jolt Winner: Perforce SCM 2005 (Perforce)
6. Modeling and Design Tools: Tools for requirements gathering, modeling, automatic code generation from models, and GUI or prototype design.
Jolt Winner: Lattix LDM 2.0 (Lattix)
7. Development Environments: Innovative programming languages, source code editors and integrated development environments (IDEs).
Jolt Winner: Visual Studio Team System 2005 (Microsoft)
8. Libraries, Frameworks and Components: Libraries of methods for enhancing security, transactions, graphical user interfaces and database connectivity; also stand-alone components, component libraries or services and COTS. Frameworks and components are systems that allow for the modification and extensions to build your application.
Jolt Winner: .NET Framework 2.0 (Microsoft)
9. Quality and Project Management Tools: Planning, analysis and scheduling tools that aid in estimating costs, program size and development effort, and managing risks and budgets. Also tools for tracking productivity metrics, recommending software development processes or best practices, and guiding technology selection.
Jolt Winner: Rally 5.6 (Rally Software Development)
10. Mobile Development Tools: Application tools for mobile handheld development, including IDEs, network management and roll-out.
Jolt Winner: Crossfire 5.6 (AppForge)
11. Security Tools: Tools for authentication and encryption, firewalls, network monitoring, system enhancements, system monitoring and spam.
Jolt Winner: Elemental Compliance System 1.4 (Elemental)
12. Testing Tools: Tools for test scripting, automation, tracking and documentation; quality assurance planning; test-case design; white- and black-box testing.
Jolt Winner: VMTN Subscription 2005 (VMware)
13. Utilities: Code optimizers and coding standards tools, build tools, platform and device emulators, code obfuscators, messaging solutions, help-authoring tools, product-demonstration tools and reverse-engineering tools.
Jolt Winner: Camtasia Studio 3.0 (TechSmith)
14. Web Development Tools: Tools for developing and deploying Web-based applications, including application servers and infrastructure to help Web applications interact dynamically with other Web applications, and tools using open standards such as XML, SOAP and HTML.
Jolt Winner: Rails 1.0 (rubyonrails.org)
查看全文elathen | 07 五月, 2006 22:05
好久没有更新Blog了,3-4月份那段时间,人感觉好累,老提不起精神,所以Blog也暂停了一段时间。从04.22-05.8,是我来公司后的第一次休的长假,主要还是因为要和女友回她老家订婚,同时也顺便休息一下调整一下自己。说是休假,当我们的行程还是安排的很紧的,女友的老家在河南的鲁山的一个山村,是个美丽的地方。4.23号我们到达郑州,郑州是我和我女友上学的地方,也是我们相识、相爱的地方。中午参加我大学室友的婚礼,他们是从大学开始的,他们从开始到现在我可以算一个见证人吧,终于走进婚礼的殿堂,真心的祝福他们。晚上,和以前的同事好友相聚了一下,品尝了一下我想了好久的烩面
。4.24日和女友逛商城,给她和自己买了一些衣服,最重要的是给女友买了一个白金戒指,一个上面有心形状的,99%的含金量,代表我对她的爱是真心真意的,也是我们订婚的见证,等结婚时一定给她买个钻戒。本来还看重一对和戒指很配的耳环,可是女友不让我买,其实我很想买,我知道她是为我省钱,当我不想委屈她
。4.27号,我们回到我女友的家,一个山青水绣的地方,空气非常好,在这里没有城市的喧闹,很舒服。女友的母亲是一个慈祥的母亲,对我像对她自己的儿子一样,让我感到很温馨。接下来几天里,我们就是走亲戚。虽然这里地处山区,有点贫穷,当是山里人的热情、朴实、善良,让我感受挺深的。和他们一起聊天,我感觉轻松愉快,希望他们将来的生活越来越好。5.5日我先回到珠海,女友要在家多陪陪妈妈几天。
明天开始上班了,也是新的开始,以后要更加努力的工作和学习。给老婆提供一个温馨幸福的家,让她快快乐乐的生活。
elathen | 04 一月, 2006 22:21
很多朋友在Java开发中,使用Oracle数据库的时候,经常会碰到有ORA-01000: maximum open cursors exceeded.的错误。 实际上,这个错误的原因,主要还是代码问题引起的。
ora-01000: maximum open cursors exceeded. 表示已经达到一个进程打开的最大游标数。 这样的错误很容易出现在Java代码中的主要原因是:Java代码在执行conn.createStatement()和conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个cursor。尤其是,如果你的createStatement和prepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因为游标一直在不停的打开,而且没有关闭。 一般来说,我们在写Java代码的时候,createStatement和prepareStatement都应该要放在循环外面,而且使用了这些Statment后,及时关闭。最好是在执行了一次executeQuery、executeUpdate等之后,如果不需要使用结果集(ResultSet)的数据,就马上将Statment关闭。 对于出现ORA-01000错误这种情况,单纯的加大open_cursors并不是好办法,那只是治标不治本。实际上,代码中的隐患并没有解除。 而且,绝大部分情况下,open_cursors只需要设置一个比较小的值,就足够使用了,除非有非常特别的要求。
对这个问题专门研究了一下,并写了测试程序,首先说一些测试环境,我们的数据库最大可打开的cursor设置为600,操作的数据库表的记录数为30条记录。为了模拟同时打开600cursor,也就是说有600个statement在运行,必须用到java的多线程。下面是测试程序:
public class StatementTest extends Thread{
private Connection conn;
public StatementTest(Connection conn) {
this.conn = conn;
start();
}
public void run(){
try {
String strSQL = "SELECT * FROM TestTable"
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(strSQL);
int i = 0;
while(rs.next()){
System.out.println("----"+i+"------");
i = i+1;
}
rs.close();
stmt.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String args[]){
try{
Connection conn = DBConnection.getConnection();
for(int i = 0;i < 800;i++){
new StatementTest(conn);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
运行这个程序,很快就会抛出ORA-01000: maximum open cursors exceeded的错误。
这次最主要的不是测试上面的问题,而是下面的问题:1. 我们通过JDBC查询,是否一次是把所有的结果集查询出来后放到ResultSet中,
2. 在调用ResultSet.next()方法的时候,是否还会和数据库交互.
其实在以前我一直认为当statement执行完sql语句后,它会把结果集保存到ResultSet中,然后关闭这个cursor,当实际上并不是这样的。我们把上面的程序修改一下就可以测试这个问题了。程序如下public class StatementTest extends Thread{
private Connection conn;
public StatementTest(Connection conn) {
this.conn = conn;
start();
}
public void run(){
try {
String strSQL = "SELECT * FROM TestTable"
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(strSQL);
int i = 0;
while(rs.next()){
System.out.println("----"+i+"------");
i = i+1;
Thread.sleep(5000);
}
rs.close();
System.out.println("resultset has closed");
Thread.sleep(10000);
stmt.close();
System.out.println("resultset has closed");
Thread.sleep(10000);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String args[]){
try{
Connection conn = DBConnection.getConnection();
new StatementTest(conn);
}catch(Exception e){
e.printStackTrace();
}
}
}当statement执行完SQL语句的时候,循环resultset的时候让线程sleep5秒钟,这样做的目的是为了有时间从数据库那方面查询这个cursor是否关闭.运行程序,在java控制台中出现System.out.println("----"+i+"------")循环信息的时候,在sqlplus中运行下面SQL语句,其中SID是程序运行对应数据库的进程ID
select sql_text from v$open_cursor where sid = 35
你会ResultSet循环的时候,运行上面的SQL,一直有结果都是"SELECT * FROM TestTable"也就是说通过statement打开的这个cursor一直没有关。可以初步判断当statement执行完sql语句后返回给resultset的结果集不是实际的结果(结果数集据不是放到JVM内存中),可能是结果集的指针,当resultset.next()的时候再通过这个指针从oracle拿数据。
为了确认这个问题,我们选了一个记录数超过10000条记录的大表,来测试,发现在整个运行时,JVM的内存没有很大的变化。这就进一步确定了上面的判断。
小结:通过上面测试和分析,我们可以的出结论通过JDBC查询结果集不是直接放到ResultSet中,ResultSet存在的可能是结果集的指针。
我们在调用ResultSet.next()时仍然需要和数据库交互。
elathen | 01 一月, 2006 23:03
elathen | 22 三月, 2005 21:48
15th Annual Software Development
Jolt Product Excellence Awards
Finalists
Books: General
Books: Technical
Business Integration and Data Tools
Change and Configuration Management Tools
Design Tools
Languages and Development Environments
Libraries, Frameworks and Components
Management Tools
Mobile Development Tools
Security Tools
Test: Automated Test Tools
Test – Defect Tracking Tools
Utilities
Web Development Tools
Websites and Developer Networks
source: http://www.sdmagazine.com/jolts/15th_jolt_finalists.html
elathen | 24 一月, 2005 21:45
elathen | 17 一月, 2005 21:32
log4j是一个优秀的开源的java日志系统,jboss内部也集成了它,在jboss下默认的只是对server做了每日日志,并没有对你部署的项目进行每日的日志构建,但我们可以通过修改log4j.xml文件来实现。log4j.xml文件在jboss安装目录下的serverdefaultconf下,打开log4j.xml文件
这是log4j默认的配置,我们先熟悉一下 <!-- ============================== --> <layout class="org.apache.log4j.PatternLayout"> <!-- Rollover at midnight each day --> <!-- Rollover at the top of each hour <layout class="org.apache.log4j.PatternLayout"> <!-- The full pattern: Date MS Priority [Category] (Thread:NDC) Message |
上面是jboss下log4j的默认配置,对jboss的server进行日志记录,接下来我们添加web项目的日志,在log4j.xml 文件中把下面的配置信息加上去就可以了
<!-- A size based file rolling appender--> <layout class="org.apache.log4j.PatternLayout">
<appender name="com.szypt.all" class="org.jboss.logging.appender.RollingFileAppender">
<param name="File" value="${jboss.server.home.dir}/log/elathen.log"/>
<param name="Append" value="true"/>
<param name="MaxFileSize" value="500KB"/>
<param name="MaxBackupIndex" value="1"/>
<param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
</layout>
</appender>
<logger name="com.szypt">
<level value="DEBUG" />
<appender-ref ref="com.szypt.all"/>
</logger>
| « | 一月 2012 | » | ||||
|---|---|---|---|---|---|---|
| 一 | 二 | 三 | 四 | 五 | 六 | 日 |
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 | |||||