Flex LCDS Data Paging

原文 :
[url]http://coenraets.org/blog/2008/05/insync-automatic-offline-data-synchronization-in-air-using-lcds-26/[/url]


Flex :
首先要下载转为LCDS提供服务的fds.swc

AIR App

<?xml version="1.0" encoding="utf-8"?>
<!-- Christophe Coenraets, christophe@coenraets.org - http://coenraets.org -->
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" layout="absolute"
showFlexChrome="false" showStatusBar="false" width="400" height="600" currentState="small"
applicationComplete="init()">

<mx:states>
<mx:State name="big">
<mx:SetProperty name="width" value="850"/>
<mx:SetProperty target="{tn}" name="visible" value="true"/>
</mx:State>
<mx:State name="small">
<mx:SetProperty name="width" value="400"/>
<mx:SetProperty target="{tn}" name="visible" value="false"/>
</mx:State>
</mx:states>

<mx:Script>
<![CDATA[
import mx.data.events.DataServiceFaultEvent;
import mx.data.DataService;
import mx.rpc.AsyncResponder;
import air.net.SocketMonitor;
import mx.messaging.channels.RTMPChannel;
import mx.messaging.Channel;
import mx.messaging.ChannelSet;
import mx.data.Conflicts;
import mx.rpc.AsyncToken;
import mx.data.events.DataConflictEvent;
import mx.data.Conflict;
import mx.controls.Alert;

[Embed(source='/assets/sync_light.swf', symbol='icon_online')]
private var iconOnline:Class;

[Embed(source='/assets/sync_light.swf', symbol='icon_offline')]
private var iconOffline:Class;

private var host:String = "localhost";
private var port:int = 2037;

private var socketMonitor:SocketMonitor;

[Bindable]
private var ds:DataService;

private function init():void
{
ds = new DataService("insync");
ds.cacheID = "insync19";
ds.autoSaveCache = false;
ds.autoConnect = false;
ds.autoCommit = false;
ds.addEventListener(DataConflictEvent.CONFLICT, conflictHandler);
ds.addEventListener(DataServiceFaultEvent.FAULT, faultHandler);

var cs:ChannelSet = new ChannelSet();
var rtmpChannel:Channel = new RTMPChannel("my-rtmp", "rtmp://" + host + ":" + port);
cs.addChannel(rtmpChannel);
ds.channelSet = cs;

socketMonitor = new SocketMonitor(host, port);
socketMonitor.pollInterval = 3000;
socketMonitor.addEventListener(StatusEvent.STATUS, networkStatusChangeHandler);
socketMonitor.start();
}

private function networkStatusChangeHandler(event:StatusEvent):void
{
trace("networkStatusChangeHandler() socketMonitor.available: " + socketMonitor.available);
if (socketMonitor.available)
{
// The network just became available.
// The dataService should not be connected at this time. But in case it is, we do not try to reconnect.
// If (as expected) the dataService is not connected, we connect before handling offline changes
if (ds.connected)
{
trace("DataService is already connected");
handleOfflineChanges();
}
else
{
connect();
}
}
else
{
if (contacts.length == 0)
{
// contacts haven't beeen retrieved yet: we are starting the application offline.
// contacts will be filled from cache.
fill();
}
}
}

private function connect():void
{
trace("Connecting...");
var token:AsyncToken = ds.connect();
token.addResponder(new AsyncResponder(
function(result:Object, token:Object = null):void
{
trace("ds.connect(): success");
// We are online: check if there are offline changes to apply.
handleOfflineChanges();
},
function(error:Object, token:Object = null):void
{
trace("ds.connect(): failure");
// We are offline: fill data from cache.
fill();
}));
}

private function handleOfflineChanges():void
{
if (ds.connected && ds.commitRequired)
{
trace("Committing offline changes...");
var token:AsyncToken = ds.commit();
token.addResponder(new AsyncResponder(
function(result:Object, token:Object = null):void
{
trace("ds.commit(): success");
Alert.show("Changes have been made offline and have been synchronized with the server", "Data Synchronization");
displayOfflineStatus();
fill();
},
function(error:Object, token:Object = null):void
{
trace("ds.commit(): failure");
Alert.show("Cannot commit offline changes", "Data Synchronization Error");
}));
}
else
{
trace("No offline changes to apply");
fill();
}
}


private function fill():void
{
// Load the contacts. If we are offline contacts will be loaded from the cache (if it exists)
trace("Filling...");
var asyncToken:AsyncToken = ds.fill(contacts);
asyncToken.addResponder(new AsyncResponder(
function(result:Object, token:Object = null):void
{
trace("ds.fill(): success");
trace("ds.saveToCache()");
ds.saveCache();
},
function(error:Object, token:Object = null):void
{
trace("ds.fill(): failure");
Alert.show("Cannot fill");
}));
}


public function openTab(contact:Object):void
{
currentState = "big";
var children:Array = tn.getChildren();
var length:int = children.length;
for (var i:int = 0; i<length; i++)
{
if (children[i].contact == contact)
{
tn.selectedIndex = i;
return;
}
}
var form:ContactForm = new ContactForm();
tn.addChild(form);
form.ds = ds;
form.contacts = contacts;
form.contact = contact as Contact;
tn.selectedChild = form;
}

public function displayOfflineStatus():void
{
var children:Array = tn.getChildren();
var length:int = children.length;
for (var i:int = 0; i<length; i++)
{
ContactForm(children[i]).displayOfflineStatus();
}
}

private function createItem():void
{
openTab(new Contact())
}

private function faultHandler(event:DataServiceFaultEvent):void
{
// fail silently for now. We catch errors on a per method basis.
trace(event.fault.faultString);
}

private function conflictHandler(event:DataConflictEvent):void
{
// TODO: provide fine grained conflict resolution options to the user.
var conflicts:Conflicts = ds.conflicts;
for (var i:int=0; i<conflicts.length; i++)
{
var conflict:Conflict = conflicts.getItemAt(i) as Conflict;
if (!conflict.resolved)
{
conflict.acceptServer();
}
}
Alert.show("There were " + conflicts.length + " conflicts. Contacts have been reloaded from the server.", "Conflict");
}

private function toggleMaximize():void
{
currentState = currentState=="small" ? "big" : "small";
}

private function childRemoveHandler():void
{
if (tn.numChildren == 1)
{
currentState = "small";
}
}

]]>
</mx:Script>

<mx:Style source="styles.css"/>

<mx:ArrayCollection id="contacts"/>

<mx:Canvas id="container" styleName="appContainer" left="12" right="12" top="12" bottom="12">

<mx:Canvas left="20" top="14" right="20" mouseDown="nativeWindow.startMove()">
<mx:Canvas backgroundColor="#006699" width="36" height="36" verticalCenter="0"/>
<mx:Label text="in" fontSize="24" color="#FFFFFF" x="7" verticalCenter="0"/>
<mx:Label text="Sync" styleName="appTitle" x="38" verticalCenter="0"/>
<mx:Button icon="@Embed('assets/icon_plus.png')" width="32" height="32" click="createItem()" x="120" verticalCenter="0"/>
<mx:Button styleName="iconMinimize" verticalCenter="0" right="26" click="minimize()"/>
<mx:Button styleName="iconMaximize" verticalCenter="0" right="13" click="toggleMaximize()"/>
<mx:Button styleName="iconClose" verticalCenter="0" right="0" click="close()"/>
</mx:Canvas>

<mx:HBox top="60" bottom="50" left="18" right="18">
<mx:DataGrid id="list" width="340" height="100%" dataProvider="{contacts}"
doubleClick="openTab(list.selectedItem)" doubleClickEnabled="true">
<mx:columns>
<mx:DataGridColumn dataField="firstName" headerText="First Name"/>
<mx:DataGridColumn dataField="lastName" headerText="Last Name"/>
<mx:DataGridColumn dataField="phone" headerText="Phone"/>
</mx:columns>
</mx:DataGrid>
<mx:TabNavigator id="tn" width="100%" height="100%" childRemove="childRemoveHandler()"/>
</mx:HBox>

<mx:HRule left="8" right="8" bottom="40"/>

<mx:Canvas left="20" bottom="17" right="20">
<mx:Image source="{ds.connected?iconOnline:iconOffline}" toolTip="{ds.connected?'Online':'Offline'}" verticalCenter="-1"/>
<mx:Label text="{ds.connected?'Online':'Offline'}" left="13" verticalCenter="0"/>
<mx:Label text="Commit required: {ds.commitRequired}" left="60" verticalCenter="0"/>
</mx:Canvas>

</mx:Canvas>

</mx:WindowedApplication>



ContactForm.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- Christophe Coenraets, christophe@coenraets.org - http://coenraets.org -->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" width="100%" height="100%">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;
import mx.data.messages.DataMessage;
import mx.rpc.AsyncResponder;
import mx.rpc.AsyncToken;
import mx.data.DataService;

private var _contact:Contact;

public function set contact(contact:Contact):void
{
if (contact != _contact)
{
_contact = contact;
if (contacts.contains(contact))
{
label = contact.firstName + " " + contact.lastName;
}
else
{
label = "New Contact";
}
displayOfflineStatus();
dispatchEvent(new Event("contactChanged"));
}
}

[Bindable(event="contactChanged")]
public function get contact():Contact
{
return _contact;
}

[Bindable]
public var contacts:ArrayCollection;

[Bindable]
public var ds:DataService;

private function save():void
{
contact.firstName = firstName.text;
contact.lastName = lastName.text;
contact.title = contactTitle.text;
contact.email = email.text;
contact.phone = phone.text;
if (!contacts.contains(contact))
{
trace("Adding contact");
contacts.addItem(contact);
}
label = contact.firstName + " " + contact.lastName;
commit();
}

private function deleteItem():void
{
ds.deleteItem(contact);
commit();
parent.removeChild(this);
}

private function commit():void
{
if (ds.connected)
{
if (ds.commitRequired)
{
trace("Online: Committing contact...");
var token:AsyncToken = ds.commit();
token.addResponder(new AsyncResponder(
function(result:Object, token:Object = null):void
{
trace("ds.commit(): success");
trace("ds.saveCache()");
ds.saveCache();
},
function(error:Object, token:Object = null):void
{
trace("ds.commit(): failure");
Alert.show("Error committing changes");
}));
}
}
else
{
trace("Offline: Saving to cache...");
var t:AsyncToken = ds.saveCache();
t.addResponder(new AsyncResponder(
function(result:Object, token:Object = null):void
{
trace("ds.saveCache(): success");
displayOfflineStatus();
},
function(error:Object, token:Object = null):void
{
trace("ds.saveCache(): failure");
}));
}
}

public function displayOfflineStatus():void
{
var newStatus:String;
switch (ds.getPendingOperation(contact))
{
case DataMessage.CREATE_OPERATION:
newStatus = "CREATE";
break;
case DataMessage.UPDATE_OPERATION:
newStatus = "UPDATE";
break;
case DataMessage.DELETE_OPERATION:
newStatus = "DELETE";
break;
case DataMessage.UNKNOWN_OPERATION:
newStatus = "NOT MOFIFIED";
break;
}
status.text = "Offline status: " + newStatus;
}

]]>
</mx:Script>

<mx:Form width="100%" height="100%">
<mx:FormItem label="Id">
<mx:TextInput id="contactId" text="{contact.contactId}" enabled="false"/>
</mx:FormItem>
<mx:FormItem label="First Name">
<mx:TextInput id="firstName" text="{contact.firstName}"/>
</mx:FormItem>
<mx:FormItem label="Last Name">
<mx:TextInput id="lastName" text="{contact.lastName}"/>
</mx:FormItem>
<mx:FormItem label="Title">
<mx:TextInput id="contactTitle" text="{contact.title}"/>
</mx:FormItem>
<mx:FormItem label="Phone">
<mx:TextInput id="phone" text="{contact.phone}"/>
</mx:FormItem>
<mx:FormItem label="Email">
<mx:TextInput id="email" text="{contact.email}"/>
</mx:FormItem>
</mx:Form>

<mx:Label id="status" right="8" left="8" bottom="38" visible="{!ds.connected}" color="#BBBBBB"/>

<mx:Button label="Close Tab" click="parent.removeChild(this)" left="10" bottom="8" width="80"/>
<mx:Button label="Save" click="save()" right="10" bottom="8" width="80"/>
<mx:Button label="Delete" click="deleteItem()" right="94" bottom="8" width="80"/>

</mx:Canvas>


Contact.as

// Christophe Coenraets, christophe@coenraets.org - http://coenraets.org
package
{
[Managed]
[RemoteClass(alias="lcds.samples.contact.Contact")]
public class Contact
{
public var contactId:int;
public var firstName:String;
public var lastName:String;
public var title:String;
public var phone:String;
public var email:String;
}
}


data-management-config.xml

<destination id="custom-contact">
<adapter ref="java-dao"/>
<properties>
<source>lcds.samples.contact.ContactAssembler</source>
<scope>application</scope>
<metadata>
<identity property="contactId" undefined-value="0"/>
</metadata>
<network>
<paging enabled="false" pageSize="10" />
</network>
</properties>
</destination>



JAVA 需要有LCDS

package lcds.samples.contact;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class BaseDAO {

// Convenience method
protected List getList(String sql) throws DAOException
{
return getList(sql, new Object[] {});
}

// Convenience method
protected List getList(String sql, int startRow, int pageSize) throws DAOException
{
return getList(sql, new Object[] {}, startRow, pageSize);
}

// Convenience method
protected List getList(String sql, Object param) throws DAOException
{
return getList(sql, new Object[] {param});
}

protected List getList(String sql, Object[] params) throws DAOException
{
return getList(sql, params, 1, 0);
}

protected List getList(String sql, Object[] params, int startRow, int pageSize) throws DAOException
{
List list = new ArrayList();
Connection c = null;
ResultSet rs = null;
PreparedStatement ps = null;
try
{
c = ConnectionHelper.getConnection();
ps = c.prepareStatement(sql);
if (params != null)
{
for (int i=0; i<params.length; i++)
{
ps.setObject(i+1, params[i]);
}
}
rs = ps.executeQuery();

for (int i=1; i<startRow; i++)
{
rs.next();
}

int count = 0;
while (rs.next() && (pageSize==0 || count<pageSize))
{
Object obj = rowToObject(rs);
list.add(obj);
count++;
}
}
catch (SQLException e)
{
e.printStackTrace();
throw new DAOException(e);
}
finally
{
ConnectionHelper.close(c);
}
return list;
}

/*
protected Object getItem(String sql, int pk) throws DAOException
{
Integer[] params = {new Integer(pk)};
List list = getList(sql, params);
if (list != null && list.size()>0)
{
return list.get(0);
}
else
{
return null;
}
}
*/

protected Object getItem(String sql, Object pk) throws DAOException
{
Object[] params = { pk };
List list = getList(sql, params);
if (list != null && list.size()>0)
{
return list.get(0);
}
else
{
return null;
}
}

protected Object rowToObject(ResultSet rs) throws SQLException
{
throw new SQLException("Row processor not implemented");
}

protected int createItem(String sql, Object[] params) throws DAOException
{
Connection c = null;
try
{
c = ConnectionHelper.getConnection();
executeUpdate(sql, params, c);
Statement s = c.createStatement();
//ResultSet rs = s.executeQuery("SELECT LAST_INSERT_ID()");
ResultSet rs = s.executeQuery("CALL IDENTITY()");
rs.next();
int pk = rs.getInt(1);
return pk;
}
catch (SQLException e)
{
e.printStackTrace();
throw new DAOException(e);
}
finally
{
ConnectionHelper.close(c);
}
}

public int executeUpdate(String sql, Object[] params) throws DAOException
{
Connection c = null;
try
{
c = ConnectionHelper.getConnection();
return executeUpdate(sql, params, c);
}
catch (SQLException e)
{
e.printStackTrace();
throw new DAOException(e);
}
finally
{
ConnectionHelper.close(c);
}
}

public int executeUpdate(String sql, Object[] params, Connection connection) throws DAOException
{
int rows = -1;
PreparedStatement ps = null;
try
{
ps = connection.prepareStatement(sql);
if (params != null)
{
for (int i=0; i<params.length; i++)
{
ps.setObject(i+1, params[i]);
}
}
rows = ps.executeUpdate();
}
catch (SQLException e)
{
e.printStackTrace();
throw new DAOException(e);
}
return rows;
}

}



package lcds.samples.contact;

public class ConcurrencyException extends Exception
{
private static final long serialVersionUID = -6405818907028247079L;

public ConcurrencyException(String message)
{
super(message);
}

public ConcurrencyException(Throwable cause)
{
super(cause);
}

public ConcurrencyException(String message, Throwable cause)
{
super(message, cause);
}
}



package lcds.samples.contact;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionHelper
{
private String url;
private static ConnectionHelper instance;

private ConnectionHelper()
{
try {
Class.forName("org.hsqldb.jdbcDriver");
url = "jdbc:hsqldb:hsql://localhost:9002/contactdb";
} catch (Exception e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
if (instance == null) {
instance = new ConnectionHelper();
}
try {
return DriverManager.getConnection(instance.url);
} catch (SQLException e) {
throw e;
}
}

public static void close(Connection connection)
{
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}




package lcds.samples.contact;

public class Contact {

private int contactId;

private String firstName;
private String lastName;
private String title;
private String phone;
private String email;

public int getContactId() {
return contactId;
}
public void setContactId(int contactId) {
this.contactId = contactId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}


}


package lcds.samples.contact;

import java.util.List;
import java.util.Map;
import java.util.Collection;

import flex.data.DataSyncException;
import flex.data.assemblers.AbstractAssembler;

public class ContactAssembler extends AbstractAssembler
{
private ContactDAO dao = new ContactDAO();

public Collection fill(List fillParameters)
{
if (fillParameters.size() == 0)
{
return dao.findAll();
}

String queryName = (String) fillParameters.get(0);

if (queryName.equals("by-name"))
{
return dao.findByName((String) fillParameters.get(1));
}

return super.fill(fillParameters); // throws a nice error
}

public Object getItem(Map uid)
{
return dao.getContact(((Integer) uid.get("contactId")).intValue());
}

public void createItem(Object newVersion)
{
dao.create((Contact) newVersion);
}

public void updateItem(Object newVersion, Object prevVersion, List changes)
{
int contactId = ((Contact) newVersion).getContactId();
try
{
dao.update((Contact) newVersion);
}
catch (ConcurrencyException e)
{
System.err.println("*** Throwing DataSyncException when trying to update contact id=" + contactId);
throw new DataSyncException(dao.getContact(contactId), changes);
}

}

public void deleteItem(Object prevVersion)
{
try
{
dao.delete((Contact) prevVersion);
}
catch (ConcurrencyException e)
{
int contactId = ((Contact) prevVersion).getContactId();
System.err.println("*** Throwing DataSyncException when trying to delete company id=" + contactId);
throw new DataSyncException(dao.getContact(contactId), null);
}
}

}



package lcds.samples.contact;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class ContactDAO extends BaseDAO
{
public List findAll()
{
return getList("SELECT * FROM contact ORDER BY last_name, first_name");
}

public List findByName(String name)
{
return getList("SELECT * FROM contact WHERE UPPER(first_name) LIKE ? OR UPPER(last_name) LIKE ? ORDER BY last_name, first_name", new Object[] {"%"+name.toUpperCase()+"%", "%"+name.toUpperCase()+"%"});
}

public Object getContact(int contactId)
{
return getItem("SELECT * FROM contact WHERE contact_id=?", contactId);
}

public void create(Contact contact) throws DAOException
{
log("Creating contact: " + contact.getFirstName() + " " + contact.getLastName());
int contactId = createItem("INSERT INTO contact (first_name, last_name, title, phone, email) VALUES (?,?,?,?,?)",
new Object[] { contact.getFirstName(),
contact.getLastName(),
contact.getTitle(),
contact.getPhone(),
contact.getEmail()
});
contact.setContactId(contactId);
log("Contact created successfully - contactId: " + contact.getContactId());
}

public void update(Contact contact) throws DAOException, ConcurrencyException
{
log("Updating contact: " + contact.getContactId() + " " + contact.getFirstName() + " " + contact.getLastName());
int rows = executeUpdate("UPDATE contact SET first_name=?, last_name=?, title=?, phone=?, email=? WHERE contact_id=?",
new Object[] { contact.getFirstName(),
contact.getLastName(),
contact.getTitle(),
contact.getPhone(),
contact.getEmail(),
contact.getContactId()
});
if (rows == 0)
{
throw new ConcurrencyException("Item not found");
}
log("Contact updated successfully");
}

public void delete(Contact contact) throws DAOException, ConcurrencyException
{
log("Deleting contact: " + contact.getContactId() + " " + contact.getFirstName() + " " + contact.getLastName());
int rows = executeUpdate("DELETE FROM contact WHERE contact_id = ?", new Object[] {new Integer(contact.getContactId())});
if (rows == 0)
{
throw new ConcurrencyException("Item not found");
}
log("Contact deleted successfully");
}

protected Object rowToObject(ResultSet rs) throws SQLException
{
Contact contact = new Contact();
contact.setContactId(rs.getInt("contact_id"));
contact.setFirstName(rs.getString("first_name"));
contact.setLastName(rs.getString("last_name"));
contact.setTitle(rs.getString("title"));
contact.setPhone(rs.getString("phone"));
contact.setEmail(rs.getString("email"));

return contact;
}

private void log(String message)
{
System.out.println("# " + message);
}

}



package lcds.samples.contact;

public class DAOException extends RuntimeException
{
private static final long serialVersionUID = -8852593974738250673L;

public DAOException(String message)
{
super(message);
}

public DAOException(Throwable cause)
{
super(cause);
}

public DAOException(String message, Throwable cause)
{
super(message, cause);
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值