[Flex]基于flex4技术从零开发flex博客系统: 4 数据存储

第四课:存储数据

关于”基于flex4技术从零开发flex博客系统”这个系列,我已经写过三课。今天在Google中搜索了一下标题, 发现有许多网站已经偷偷转载了。转载没有问题,但是拜托,请附上原文链接好不好,这不仅是对作者的尊重,也是对读者的负责。昨天我对前三课进行了一些相当 程度的更新,由于这些转载者的小气,他们的读者是不可能获取更新了。不过没有关系,有意继续关注的朋友,只要在Google中搜索”基于flex4技术从零开发flex博客系统“就可以找到这个系列的最新版本,相信我的博客凭借原创以及等于5的Google PR值,会一直稳居搜索排行之首。

通过前三课我艰苦卓绝的努力,客户端与服务端通讯已经没有问题了。这对于一个没有学过flex4,没有用过java的初学者,已经相当不容易了。到目前为止,开发博客系统的准备工作,已经仅剩最后一项了:数据存储。

Google App Engine没有数据库的概念,不过app engine提供了JDO存储接口,google充许开发者直接定义、存储、查询、修改实体(entity)。

一,数据定义

我在sban.flexblog package下添加一个名为Greeting的实体类,这个一个POJO(Plain Old Java Object),意即简单朴素的java对象。Greeting.java类的代码如下:

package sban.flexblog;

import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

/**
 * @author sban.li
 *
 */

@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class Greeting {

        @PrimaryKey
        @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
        private Long id;
       
        @Persistent
        private String user;
       
        @Persistent
        private String greetingContent;
       
        @Persistent
        private Date date;
       
        public Greeting(String user, String content, Date date)
        {
                this.user = user;
                this.greetingContent=content;
                this.date = date;
        }
       
        public String getUser()
        {
                return this.user;
        }
       
        public Long getId()
        {
                return this.id;
        }
       
        public String getGreetingContent()
        {
                return this.greetingContent;
        }
       
        public Date getDate()
        {
                return this.date;
        }
}

关于Pojo实体定义的简要说明:
1,@PrimaryKey用于定义实体的主键
2,@Persistent用于标识该变量名称将被存储
3,valueStrategy=IdGeneratorStrategy.IDENTITY设置id为自增模式,这意味着实例化该实体类时不用给id赋值
4,identityType=IdentityType.APPLICATION是干什么用的,不清楚,暂时不用管它

二,数据存储

实体定义好了,如何存储呢?这要用到javax.jdo.PersistenceManager,在sban.flexblog.managers package下新建一个类,名为PMFactory.java,代码如下:

package sban.flexblog.managers;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

/**
 * @author sban.li
 *
 */

public class PMFactory {

        private static final PersistenceManagerFactory pmfInstance = JDOHelper
                        .getPersistenceManagerFactory("transactions-optional");

        private PMFactory() {
        }

        public static PersistenceManagerFactory getInstance() {
                return pmfInstance;
        }

}

该工厂用于返回一个PersistenceManagerFactory的单例,其中transactions-optional参数是在src/META-INF/jdoconfig.xml中定义的:

<persistence-manager-factory name="transactions-optional">

   </persistence-manager-factory>

但是PersistenceManagerFactory并不是数据的管理者,PersistenceManager才是,现在我们可以用 PMFactory.getInstance().getPersistenceManager()方法获取PersistenceManager了。

我们需要提供一个新接口,这个接口充许用户提交用户名与greeting内容参数,并存储到google app engine。修改HelloWorld.java,添加如下代码:

private PersistenceManager pm = PMFactory.getInstance()
                        .getPersistenceManager();

        public Boolean greet2(String user, String content) {
                Boolean result = true;
                Greeting greeting = new Greeting(user, content, new Date());

                try {
                        pm.makePersistent(greeting);
                } catch (Exception e) {
                        result = false;
                } finally {
                        pm.close();
                }

                return result;
        }

pm.makePersistent用于存储Greeting实例对象。使用之后记得要记得调用pm.close。该接口需要两个参数,一为用户名称,一为greeting内容。如果存储成功,将返回true。

三,客户端代码调用

好了,server端已经准备就绪,现在客户端可以调用了。修改Index.mxml,修改greetViaRemoting方法,代码如下:

<FxApplication xmlns="http://ns.adobe.com/mxml/2009" initialize="configRemoting()">

        <Script>
                <![CDATA[
                        import mx.controls.Alert;
                        import flash.net.URLLoader;
                        import flash.net.URLRequest;
                        import mx.rpc.events.ResultEvent;
                        import mx.rpc.events.FaultEvent;
                        import mx.rpc.AbstractOperation;
                        import mx.rpc.remoting.RemoteObject;
                        import mx.messaging.ChannelSet;
                        import mx.messaging.channels.AMFChannel;
                        import mx.collections.ArrayCollection;
                        import mx.managers.CursorManager;
                       
                        [Bindable]
                        private var _greetingData : ArrayCollection;
                        private var _remotingObj : RemoteObject = new RemoteObject("GenericDestination");
                       
                        private function configRemoting() : void
                        {
                                _remotingObj.source = "sban.flexblog.HelloWorld";
                                _remotingObj.endpoint = "weborb.wo";
                        }
                       
                        private function greetViaRemoting() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("greet2");
                                op.addEventListener(ResultEvent.RESULT,
                function(event : ResultEvent) : void
                                        {
                                                Alert.show( event.result.toString() );
                                        };
             );
                                op.send(vNameTxt.text,vContentTxt.text);
                        }
                ]]>

        </Script>
       
        <layout>
                <BasicLayout />
        </layout>
       
        <VGroup width="100%">
                <HGroup>
                        <Label text="user:" />
                        <FxTextInput id="vNameTxt" text="sban" />
                </HGroup>
                <HGroup>
                        <Label text="content:" />
                        <FxTextInput id="vContentTxt" text="greeting content" />
                </HGroup>
               
                <HGroup>
                        <FxButton id="vSendBtn" label="remoting greet" click="greetViaRemoting()" />
                </HGroup>
               
        </VGroup>
       
        <TextBox text="by sban" color="gray" bottom="10" right="10" />
       
</FxApplication>

其中configRemoting将在Application initialize时调用,用于初始化RemoteObject对象。BasicLayout用于声明当前Application使用absolate布局,详见new language tag Private and layout in flex4

编译,运行,点击greeting,弹出如下提示框:
alert-ok.png

四,有问题,需要调试了

貌似没有问题,貌似数据已经存进Google了。但是第二次单击提交,就有问题了,Alert了两次。为什么点击一次button,弹出了两个提示框?难道接口被调用了二次?

为了找到原因所在,我必须要采用Debug模式了。在本地采用Debug模式之前,需修改Flex Project的属性里的output folder url为http://localhost:8080/,并把RemoteObject的endpoint设为http: //localhost:8080/weborb.wo。但是在正式布署到Google app engine时,需记得移除这些设置。

通过debug,我发现greet2接口确实仅调用了一次,但是Alert.show却执行了两次。这说明AbstractOperation是一 个引用对象,第一次调用_remotingObj.getOperation(”greet2″)返回的对象与第二次调用返回的对象是同一个对象,我们在 同一个对象上添加了两次ResultEvent事件监听。handler做为内嵌函数变量,虽然前后名称相同,但因是局部函数变量,所以内部ID并不相 同,是两个完全不同的对象。结果便是,每单击一次,Alert便会多一次。

为了验证我的想法,我查看了RemoteObject的sdk源码,在其及其父类AbstractService中,有如下相关代码:

override public function getOperation(name:String):AbstractOperation
    {
        var op:AbstractOperation = super.getOperation(name);
        if (op == null)
        {
            op = new Operation(this, name);
            _operations[name] = op;
            op.asyncRequest = asyncRequest;
        }
        return op;
    }

    public function getOperation(name:String):AbstractOperation
    {
        var o:Object = _operations[name];
        var op:AbstractOperation = (o is AbstractOperation) ? AbstractOperation(o) : null;
        return op;
    }

由上面源码可以看出,在第一次调用时,flex会实例化一个Operation,并缓存在_operations之中。第二次,便是直接取出了。
思考1:读者可以思考一下,为什么flex要在父类中取出AbstractOperation引用,而在子类中实例化Operation并存储。

签于以上原因,我修改了我的greetViaRemoting的代码,如下:

private function greetViaRemoting() : void
{
        var op : AbstractOperation = _remotingObj.getOperation("greet2");
        var handler : Function = function(event : ResultEvent) : void
                {
                        op.removeEventListener(ResultEvent.RESULT, handler);
                        Alert.show( event.result.toString() );
                };
       
        op.addEventListener(ResultEvent.RESULT, handler);
        op.send(vNameTxt.text,vContentTxt.text);
}

修改之后,便没有重复弹出的问题了。

sban:在flex开发中,使用之后的事件监听一定要记得移除,特别对于通过内嵌函数添加的事件监听。反复的移除与添加事件监听,并不会 影响程序性能,相反如果只添加而不移除,才会让程序运行愈加沉重。这就好比做人,财富、名誉、资历、爱情等等这些身外之物,多了并不是负担,但要是每一样 都放在心里,就累了。

有些学者(学习者)可能说了,貌似数据存储成功了,但是我并不知道到底有没有真正存进Google云啊,怎么查看我存储的数据呢?这些存储如何更 新,如何删除?可惜Google并没有直接给我们开放直接访问App Engine数据库的权限。在我想继续描述app engine是如何让开发者查询、修改、删除数据时,我发现这一课已经写的相当长了。我的肩膀有点酸了,我想我应该去健身了。读者们也需要体息了。

本课最终源码:见下一课源码
sban 2009/4/26 于北京
本文为sban原创,作者保留所有权利,如需转载请保留作者原文链接
flex选修课系列:基于flex4技术从零开发flex博客系统

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏