1. Java – JDBC – MySql
1. 装好MySql, 启动服务.
2. 将mysql-connector-java-5.1.7-bin.jar 拖入WEB-INF/lib下.
搞定.
2. Java端BlazeDS配置
1. Java端:
将BlazeDS压缩包内的WEB-INF里面的东西拷贝工程内的WEB-INF下的相应位置中.[ Flex目录下有四个xml配置文件, lib下是需要使用的jar] 包括web.xm
2. 配置services-config.xml中channels标签中的内容:
false true 4
其中, http://localhost:8080/DepartmentManagement/ 是该Web工程的地址 [可在web-content下建立一空index.html, 运行后查看地址便可]
3. 编写Java类, 并配置remoting-config.xml文件的<service>标签下, 增加服务, 如下:
com.insprise.guoliang.DepartmentManagement
3. Flex端建立工程
1. 建立 Flex与Java通信载体RemoteObject
为方便在整个工程中使用, 可建立一个Singleton – 关于AS中的Singleton可见: http://liguoliang.com/2008/10/128/
在该类中建立RemoteObject.
首先需要确定RemoteObject的destination, 在本例中 为”DepartmentManagement”; – 在remoting-config.xml中已配置.
其次需要确定ro的channel – 本例中为:”http://localhost:8080/DepartmentManagement/messagebroker/amf"; - 在services-config.xml中已配置
代码:
public static const DEFAULT_DEST:String = "DepartmentManagement"; public static const DEFAULT_CHANNEL_URL:String = "http://localhost:8080/DepartmentManagement/messagebroker/amf"; private static var _ro:RemoteObject; /** * Constractor - Singleton */ public function AppContext():void { throw new Error("AppContext is Singleton!"); } /** * Get RemoteObject */ public static function getRemoteObject():RemoteObject { if(_ro == null) { _ro = createRemoteObject(DEFAULT_DEST, DEFAULT_CHANNEL_URL); } return _ro; } /** * Constructs a new remote object with new channel. * @param roDestination Destination of the RemoteObject; should match a destination name in the services-config.xml file. * @param channelURI the URI used to create the whole endpoint URI for this channel; this uri can be relative uri (to the folder containing the SWF). * @param channelId the id of the channel, if set to null, a random Id will be assigned. */ protected static function createRemoteObject(roDestination:String, channelURI:String, channelId:String = null):RemoteObject { var channelSet:ChannelSet = new ChannelSet(); var channel:AMFChannel = new AMFChannel(channelId == null ? "channel-" : channelId, channelURI); //Create new Channel channelSet.addChannel(channel); var ro:RemoteObject = new RemoteObject(roDestination); ro.channelSet = channelSet; return ro; }
4 配置完成,.
在Java端建立相关的Class,
启动服务器.
在Flex端通过 Appcontext.getRemoteObject.getOperation(“方法名称”)来调用服务器端方法.
具体实例:
Java端的Class – 通过JDBC 读取 MySql中数据:
/** * Load all Department and return an ArrayList * @return */ public ArrayList loadDepartments() { ArrayList departmentsAL = new ArrayList(); log.info("Loading Departments..."); try { //Get Connection Connection conn = JdbcUtilities.getConnection(); //Create statement String sql = " SELECT * FROM Department d ORDER BY d.Department_ID"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet res = ps.executeQuery(); log.debug("Exectuing: " + sql); while (res.next()) { int id = res.getInt("Department_ID"); String name = res.getString("name"); Department dp = new Department(); dp.setId(id); dp.setName(name); departmentsAL.add(dp); log.debug("从数据库获得部门: " + dp); } JdbcUtilities.closeConn(res, ps, conn); log.info("加载部门信息结束, 共加载部门: " + departmentsAL.size()); } catch (Exception e) { log.error("SQL Error", e); throw new Error(e); } return departmentsAL; }
Flex端的代码:
/** * Load the Department's Employee */ private var op:AbstractOperation; public function loadEmployees():void { op = AppContext.getRemoteObject().getOperation("loadEmployees"); //获得Operation op.arguments = [id]; //设定参数 var at:AsyncToken = op.send(); //Send at.addResponder(this); //为本实例增加responder }
Flex端responder剩余代码见:http://liguoliang.com/2009/02/777/
运行Flex工程, Flex取得RemoteObject, 然后通过channel建立到destination的连接,由Java读取数据库中信息, 并返回给Flex.
Bindable通过Event机制来实现. 详见: http://liguoliang.com/2008/09/104/
但是在该过程中, 时常会因不小心变更对象 导致Bindable无法继续. 如下:
某DataGrid, 其dataprovider为 studentsAC – ArrayCollection
如下:
var studentsAC = new StudentAC();
dataGrid.dataprovider = studentsAC;
此后对studentAC进行任何操作都会及时在DataGrid中显示出来.
但如果进行
studentsAC = getStudentsACFromServer();
DataGrid将不会进行更新, 应使用:
dataGrid.dataprovider = getStudentsACFromServer(); ,
或选择将getStudentsACFromServer();中内容添加到studentsAC 中.
初始化完成后:
点击左侧目录后:
具体实现:
初始化时:
//Main - Maincontent vboxMainRightcontent = new VBox(); empListContainer = new MainContent(); empListContainer.visible = false; vboxMainRightcontent.addChild(empListContainer); //----------------------------加入button 测试 invilidate...---------- testButton.label = "我是来测试的"; vboxMainRightcontent.addChild(testButton); empListContainer.explicitHeight = 0;//注意此处, 设置为0方可不占用界面中的场地, 注意在visable设置为true之后还要进行相应的调整 empListContainer.explicitWidth = 0;
左侧目录被点击后:
//---------------------------------Listeners------------------------ //Listener - onListChange private function onDepListChange(e:ListEvent):void { _empListContainer.visible = _treeDepCat.selectedItem != null; if(_empListContainer.visible) { _empListContainer.explicitHeight = undefined;//将explicitHeight复原, 否则虽然已经visable, 但其宽高都是0 仍无法可见. _empListContainer.explicitWidth = undefined; }else { _empListContainer.explicitHeight = 0; _empListContainer.explicitWidth = 0; }
要求: 如下编辑器:
编辑职员信息, 在验证其输入均合法之后, 并判断该对象是否需要保存 – 如果没有改变, 则不需要保存
分析:
1. 可继承Vbox等组件设计该Edtior
主要进行创建, 添加组件等操作…
2. 设定编辑对象, 设定正在编辑的职员, 并将相关信息输出到Editor中.
/** * 给定正在编辑的职员 */ public function set editorData(editorData_:Employee):void { _editorData = editorData_; textID.text = editorData_.id.toString(); textName.text = editorData_.name; textAge.text = editorData_.age.toString(); textAddress.text = editorData_.address; if(editorData_.isMale > 0) { if(editorData_.isMale != 1) { _radioNotMals.selected = true; }else { _radioIsMale.selected = true; } }else {//新建情况下 两个readiobutton均不选择 - 通过validation提示用户选择 _radioIsMale.selected = false; _radioNotMals.selected = false; } if(editorData_.level > -1 ) { comboLevel.selectedIndex = editorData_.level; }else {//新建时 comboLevel.selectedIndex = 0; } validateAll(); }
3. 验证输入数据, 是否合法
见: http://liguoliang.com/2009/02/780/
4. 判断对象是否有改动, 若有改动则enable Save Button. 否则继续unable.
//---------------------------设置Save Button的可用性, 输入合法, 并且至少有一项是dirtyt的才能enable------------------------------ private function setButton():void { if(_editorData == null) { return; } var allValidate:Boolean = (textName.errorString == null && textAge.errorString == null && hboxIsMale.errorString == null && comboLevel.errorString == null); var anyOneDirty:Boolean = (_editorData.name != inputName || _editorData.age != inputAge || _editorData.isMale != inputIsMale() || _editorData.level != inputLevel() || _editorData.address != inputAddress()); if(allValidate && anyOneDirty) { buttonSave.enabled = true; return; } buttonSave.enabled = false; }
输入通过验证, 并检测到数据发生变化时, Enable Save Button:
按照MVC, Save及Cancel等操作应交由Control进行操作.
1. get Operation
2. set arguments
3. AddResponder, Send();
如下:
private var op:AbstractOperation; public function loadEmployees():void { op = AppContext.getRemoteObject().getOperation("loadEmployees"); //获得Operation op.arguments = [id]; //设定参数 op.send(); var at:AsyncToken = op.send(); //Send at.addResponder(this); //为本实例增加responder } //使用当前类响应, 则应实现IResponder接口, 要实现两个方法, 如下: //---------------------Responder------------------------------- public function result(responderResult:Object):void { var resultEvent:ResultEvent = responderResult as ResultEvent;//转换 var ac:ArrayCollection = resultEvent.result as ArrayCollection;//获得Result log.debug("已读取到 " + this + "的职员列表, 职员数目为: " + ac.length); } public function fault(data:Object):void { throw new Error("远程操作失败"); }
原先的错误方法:
private var op:AbstractOperation; public function loadEmployees():void { op = AppContext.getRemoteObject().getOperation("loadEmployees"); //获得Operation op.arguments = [id]; //设定参数 op.addEventListener(ResultEvent.RESULT, onSqlResult,false, 0, true); op.send(); // var at:AsyncToken = op.send(); //Send //at.addResponder(this); //为本实例增加responder } private function onSqlResult(e:ResultEvent):void { var ac:ArrayCollection = e.result as ArrayCollection; for each(var emp:Employee in ac) { emp.department = this; employeeAC.addItem(emp); } log.debug("已读取到 " + this + "的职员列表, 职员数目为: " + ac.length); op.removeEventListener(ResultEvent.RESULT, onSqlResult);//更错误的方法是没有remove Listener }
这样导致该方法被呼叫时, op都会被监听, 成功后取消监听, 如果不取消监听, 则会出现如下输出信息:
第一次调用:
[DEBUG] Department 已读取到 Dep1的职员列表, 职员数目为: 2
第二次调用:
[DEBUG] Department 已读取到 Dep1的职员列表, 职员数目为: 2
[DEBUG] Department 已读取到 Dep2的职员列表, 职员数目为: 2
第三次调用:
[DEBUG] Department 已读取到 Dep1的职员列表, 职员数目为: 0
[DEBUG] Department 已读取到 Dep2的职员列表, 职员数目为: 0
[DEBUG] Department 已读取到 Department3的职员列表, 职员数目为: 0
在此情况下, op可视为一类操作, 这样在使用op[他会有很多个实例, 会有很多个对应的监听函数]监听之后, 每个实例的Result的返回都会引发所有实例的Listener运行. 因此会出现上面的状况.
// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.