|
|
Archive for April, 2009

On Monday, Adobe announced Flash Lite 3 platform to be a part of new TV sets/cable boxes – coming mid 09. It can be big breakthrough – even revolution – for the consumers. Or, without enough independent players, it can just be a neat way for cable providers to minimize the cost of targeted advertisement/services at the cost of upgrade of set-top boxes – most of them are being replaced this year anyway.
Giving programmable environment to the TV will be as freeing and devastating as Internet publishing for the traditional paper one. Traditional TV is best described by George in early Seinfield : “People will watch the show about nothing because it is on TV”. New environment can completely change not only HOW we receive the entertainment but WHAT and WHEN and HOW MUCH we will watch. Here is how it it works for me.
(Disclaimer: I have not been Cable TV subscriber for the last 8 years – after all I have kid at home. I watch movies that I choose, listen to music libraries collected for over 20 years, and share photos with my friends going back to black and white film. My base entertainment system is built around Macs network with every possible audio and video component not to mention musical instruments).
So last weekend I went to my brother birthday and gave him his first Mac Mini as a present. However, I did not plug it into his office monitor. Instead, it went straight into FIOS connection and HDTV unit. It had bluetooth Logitech DiNovo Edge keyboard and WII remote for mouse (kids preferred to use iPod). Most importantly, it had archive of family resources and was pre configured to securely access my “shared” libraries and gave me access to his “shared” ones. Plus instant video communications, screen sharing and VM remoting. For advanced uses, grid computing and documents backup.
The result was very enthusiastic from mostly skeptic (burned by Vista/Media Center fiasco) audience. Seeing that small box actually working caused quite a stir. I am pretty much set with birthday wish list for the next year. The simple fact that I can share my media and have “family” network and broadcasting, play human games like chess/cards/backgammon while seeing/talking to the opponent has great value to me – with friends and family all over the globe.
There is one small problem – the price for average family. The modern unit with backup drive and all extras costs ~$900. Flash on set-top allows for $0 cost (Ok, $200-300 for decent wireless keyboard and mouse and terabyte storage) while delivering most of the functionality of the above unit. Giving top boxes with HDTV ability to replace computers and TV programming can be a tipping point for the new media and relationship software.
Recently, just about 50% of Americans said that TV is “necessity” – more then 15% drop from 10 years ago. Most of the drop is accounted by the fact that younger generation is getting their entertainment via different (computer based) sources. As growth in facebook and myspace population is currently fueled by people around retirement age, HDTV based living room offers much more comfortable environment for mainstream adoption
I do not really care for twitter from anyone – especially paid editors to bombard me with clever sales pitches of 140 characters or less. If they have some thoughts to share, lets keep it real – 300 words at least, I would rather read or better yet listen to podcast/youtube from the people I trust. Trust is the key here. Once there is enough alternative information, the whole media structure is going to change. Advertisement as we know it will not work if people have alternative and more trusted source of information. In the end who do you trust – Amazon reviews or magazine editors dependent on the advertisement of the products they review?
Internet has not fulfilled so many predictions of the past millennium – like destroying the malls and making home shopping personalized and fun experience – but it will happen soon enough with the current economy. With so many people becoming disposed by Wall Street and Madison Ave the Silicon Alley gets all necessary talent and technology. It is going to be fun year, with more startups getting into RIA and streaming technologies – and I am looking forward with playing with new ideas and approaches to make programming more humane experience.
Sincerely,
Anatole Tartakovsky
Permalink

Today we’ve finally submitted chapter 6 of the book on Enterprise development with Flex to O’Reilly. This one was about advanced techniques of using BlaseDS in communications between Flex and Java. At this point you’d expect something like, “It’s coming out nicely”. Sorry guys, I’m not a modest person. But I’m honest.
Here me out: this chapter 6 (66 pages) on advanced techniques of Flex/Java communications using open source BlazeDS is a gem. This chapter alone is worth buying the book. Server side push over AMF, reverse side RPC, automatic data synchronization…
I have the best co-authors. Ever. They’re just amazing. Yes, we fight with each other. We don’t agree. We are not politically correct. We use the f-word when discuss enterprise architecture. We want to find the best possible solution for our customers. We publish thought provoking articles (some people call them controversial, but they are freaking wrong). But guess what, we’ve been there. And I’m not talking about sales presentations trying to convince you that Flex/Java does your body good. “Take a look… it’s just 20 lines of code and we populated the data grid with XML coming from the server”. It’s so sweet….
I’m talking about the real stuff that Wall Street is dealing with day in and day out. OK. Forget about Wall Street. They are still out of style. Let’s talk about the calls like this one, “We are going live in two months. Can you please take a look at our application it doesn’t perform that great.”
Oh, really….It doesn’t perform well? Don’t kid yourself. It’s dead in the water. I know, I know. You’ve outsourced the development. It seemed so easy to drag and drop Flex components… You didn’t get a chance to schedule stress tests yet? How many? Ten thousand users will play online roulette…? We’ll do our best.
I don’t know why O’Reilly decided to sell this book for stinking fifty bucks. We don’t have a say in pricing. But it should cost a lot more than that.
Sorry, I’m not a modest person, but at least I’m honest.
Yakov
Permalink

Upcoming release of the ClearDataBuilder supports AUTOINCREMENTed property in the DataCollection items populated from databases based on autogenerated values like MSSqlServer/ Sybase identity, or Oracle’s sequence, and so forth. All you need to do is add identity parameter to the CDB annotation
/**
* @daoflex:sql
* sql=select * from employee
* transferType=EmployeeDTO[]
* keyColumns=id
* identity=id
* updateTable=employee
*/
public abstract List getEmployees();
and allow server changes to reflect back on the DataCollection:
dataCollection.roundTripSync = true;
Sample Use Case
Imagine a MSSqlServer table “employee”, with IDENTITY column named “id”. Here is the abstract Java class you would need to write:
package com.farata.test;
import java.util.List;
/**
* @daoflex:webservice
* pool=jdbc/test
*/
public abstract class Employee
{
/**
* @daoflex:sql
* sql=select * from employee
* transferType=EmployeeDTO[]
* keyColumns=id
* identity=id
* updateTable=employee
*/
public abstract List getEmployees();
} |
A Clean CDB build, as usual, will generate concrete implementation of the Assembler, already registered for Flex remoting, ActionScript DTO classes matching the Java counterparties and so forth.
Importantly, the specifics of the identity annotation will force CDB to enquire the value of the autogenerated id column along with the execution of the executeUpdate() and send the modified record back to the Flex client.
Two-Way Syncronization: DataCollection – BlazeDS Assembler
You have to allow the echo of the server-side changes back to the client. You have two options.
dataCollection.roundTripSync=true; – to allow Flex process the immediate result of “your own” dataCollection.sync() call
dataCollection.autoSyncEnabled=true; – to allow Flex consume server-born asyncronous messages with changes inflicted by other clients. This option requires design-time autoSyncEnabled=true CDB annotation as well.
Here is the explanation. During the sync() operation changes originated in the DataCollection are remoted to method(s) of a server Assembler as Array of com.farata.remoting.ChangeObject elements. All of these methods are also returning the Array of changes, including, if any, changes originated by the server. The value of the roundTripSync determines whether these changes effect DataCollection when the synchronization is complete. Default value is false
Pluggable Identity Factory
No two databases are born equal. The default setting of the CDB is to handle identity compliant with MSSqlServer, i.e. to use SELECT SCOPE_IDENTITY() as the cleanest possible computation of the identity. If you are running Sybase, you would have to modify the default daoflex.identity.factory in the daoflex-build/daoflex-build-custom.properties:
daoflex.identity.factory=com.farata.daoflex.SybaseIdentityFactory |
It gets worse if you are an Oracle sequence guy. Plugging a foo.bar.OracleIdentityFactory class that has to implement com.farata.daoflex.IIdentityFactory interface is not any different from the Sybase, but CDB does not know upfront which sequence to use. So roll up your sleeves and create something like following:
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
32
33
34
35
36
37
38
39
| package foo.bar;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import com.farata.daoflex.DAOException;
import com.farata.daoflex.IIdentityFactory;
public class OracleIdentityFactory implements IIdentityFactory {
public long getIdentity(Map<String, Object> properties) {
long identity = 0;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
Connection conn = (Connection) properties.get(CONNECTION);
String table = (String) properties.get(TABLE);
String sequenceName="UNKNOWN_SEQUENCE";
// Your custom logic to determine the sequence give the table goes here
if (table==table)
sequenceName = "MY_SEQUENCE";
stmt = conn.prepareStatement("SELECT " + sequenceName + ".NEXTVAL AS identity FROM DUAL");
rs = stmt.executeQuery();
while( rs.next() ) {
identity = rs.getLong("identity");
}
return identity;
} catch (Throwable te) {
te.printStackTrace();
throw new DAOException("Failed creating identity", te);
}
finally {
try {rs.close(); rs = null;} catch (Exception e){}
try {stmt.close(); stmt = null;} catch (Exception e){}
}
}
} |
Victor Rasputnis
Permalink
We’ll be a two day training workshop “Flex for Architects” that will take place in Boston, MA on May 21-22, 2009.
During registration at http://www.eventbrite.com/event/295389518 , enter the discount code flex100 to get $100 of the tuition.
If you are about to start new Java Enterprise project, attending this workshop will definitely save time and money.
Permalink
I’ll be in Ukraine on a private visit. Microsoft and Sun Microsystems made arrangements, and I’ll be presenting on their joint meeting reviewing technologies for RIA development (AJAX, Flex, Silverlight, JavaFX). This event will take place on May 4, 2009 in the offices of Microsoft Ukraine. My special thanks to Aleksandr Oreshnikov from Microsoft Ukraine for making it happen.
If you live in the area, you can register for the event at http://www.developers.org.ua
It’s going to be an interesting experience for me as I’ll need to present in Russian – most likely this talk will be heavily sprinkled with English terms, but I’m sure Ukrainian developers will forgive me for this.
Yakov Fain
Permalink

Is your RIA fast? I mean not when your are running it locally on your PC that has the Browser, Web server and the database installed, but when it’s out in the wild where people in the USA and Europe “enjoy” DSL speed of 512Kbps. The countries like India or Russia use the term “broad band” if the network that can deliver 128Kbps.
After all optimizations and modularization your newborn Flex RIA weighs 1MB, plus a number of libraries that may be lazy loaded after the user starts working with your application. Last year it was acceptable. Today, I think we can do better.
Besides fine tuning of each and every loadable module that will improve actual performance of your application, there’s something that’s known as perceived performance. The speed of loading of the home page of your application is crucial in forming the user’s opinion about performance of your application.
If the home page won’t show up within 20 seconds, a typical user will say that performance sucks.
Adobe Flex is a great tool, but Flex SDK is heavy. Even though you can link Flex SDK to your SWF as an RSL hoping that the user already have it cached locally, there’s always this first time when the SDK has to arrive to the client.
So what’s the reasonable size of the home page of an RIA? IMO, it should weigh less than 200Kb. Is it possible with Flex SDK? I doubt it unless you are in business of selling Hello World applications. In the past, we were trying to improve user perception by introducing pure Flash/AS splash or logon screens on the preinitialize event, but now I believe that we should take more radical architecture approach – create an entire home page in Flash without using Flex SDK API. Then the Loader should be able to load SWF/SWC files created in Flex and Flash/Flex code can communicate via events.
Development of a home page in pure ActionScript will take a lot more time than in Flex, but isn’t making the users happier the ultimate goal of any RIA?
Yakov Fain
Permalink

Upcoming release of the ClearDataBuilder supports deep synchronization of hierarchical DataCollections with the Server, so that if an item of the collection contains child collections and so on, the entire tree of changes, if any, gets synchronized with the Java back end in one transaction:
orders.sync(true); // Deep sync of the DataCollection and all nested children
Sample Use Case
Listing 9 presents two SQL tables Orders and OrderItems. Sample application enables end user to navigate from order to order, editing the master information (order) as well as details (order items). All interactive changes are accumulated in Flex – until button “Commit” is clicked. That’s exactly when deep sync happens – in one transaction, i.e. all or nothing, “commit” of all changes or complete “rollback”:

Figure 1. Order-OrderItems application snapshot
Client Data Persistance
Two DataCollections – OrderCollection, Listing 2 and OrderItemCollection, Listing 3 – assist in persisting the data on the client; they remote to the methods of the Java class OrderAssembler generated by the ClearDataBuilder given SQL-annotated abstract class Order.java, Listing 8. Each item of the OrderCollection carries orderItems referring to the child collection of line items of this order. At the application level, Listing 1 below, we expose only the master collection orders, which holds the entire hierarchy:
<?xml version="1.0" encoding="UTF-8"?>
<!--OrderEntryDemo.mxml -->
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*" xmlns:collections="collections.*">
<collections:OrderCollection id="orders"/>
<mx:ControlBar>
<mx:Button label="Fill" click="selectedOrder=null;orders.fill()" />
<mx:Button label="Commit" click="orders.sync(true)"
enabled="{orders.commitRequired}" />
</mx:ControlBar>
<mx:VDividedBox >
<OrdersPanel id="master" orders="{orders}"
orderSelectionChange="selectedOrder = event.order"
/>
<OrderItemsPanel id="detail" width="100%"
selectedOrder="{selectedOrder}"
/>
</mx:VDividedBox>
<mx:Script>
<![CDATA[
import com.farata.test.dto.OrderDTO;
[Bindable] private var selectedOrder:OrderDTO;
]]>
</mx:Script>
</mx:Application> |
Hierarchical DTO Items
Not that you would not have to write any code. For one, you would have to make sure that your DTOs implements com.farata.collections.dto.IHierarchicalDTO interface, i.e. “get” accessor of childCollections. One way to do it is via helper class com.farata.collections.dto.HierarchicalDTOAdapter. Please note OrderDTO in the snippet below extends the similarly named class prefixed with the “_” symbol. This is how, by default, CDB names a pair of generated ActionScript DTOs: the class with the “_” gets regenerated by CDB on every “Clean” of the project, while the extending class is preserving manual edits. Also, do notice how child collection orderItems – for each item – gets filled asynchronously, on order_id setter:
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
| package com.farata.test.dto{
import collections.OrderItemCollection;
import com.farata.collections.dto.HierarchicalDTOAdapter;
import com.farata.collections.dto.IHierarchicalDTO;
[RemoteClass(alias="com.farata.test.dto.OrderDTO")]
public class OrderDTO extends _OrderDTO implements IHierarchicalDTO{
[Transient] [Bindable] public var orderItems:OrderItemCollection;
[Transient] public var adapter:HierarchicalDTOAdapter;
public function OrderDTO() {
super();
adapter = new HierarchicalDTOAdapter(this);
orderItems = new OrderItemCollection();
adapter.addCollection(orderItems);
}
public function get childCollections():Array {
return adapter.childCollections;
}
public override function set order_id(orderId:String):void {
if (orderId !== super.order_id) {
super.order_id = orderId;
orderItems.fill(order_id);
}
}
}//OrderDTO
} |
Browse Listings 1-8: except “view”-Panel components there is no other code you would need to write. As long a you start with the SQL-annotated abstract Java source everything else gets code generated by the CDB.
What if you use CDB with the existing DAO, i.e. IJavaDAO?
You are not confined to SQL-annotations to use the fill() and sync(), of course. CDB allows your DataCollections to remote to any Java class implementing com.farata.daoflex.IJavaDAO interface (see CDB documentation for more details):
package com.farata.daoflex;
import java.util.List;
import flex.data.ChangeObject;
public interface IJavaDAO {
List fill();
List<ChangeObject> sync(List<ChangeObject> items);
} |
In addition to IJavaDAO, participation in the deep sync requires your Java class to implement IBatchTransactionServiceSupport. Methods of this interface allow to synchronize changes accumulated by the DataCollection in three independent steps: all deletes, all updates and all inserts:
package com.farata.daoflex;
import java.util.List;
import flex.data.ChangeObject;
public interface IBatchServiceTransactionSupport {
List<ChangeObject> deleteItems(List<ChangeObject> items);
List<ChangeObject> updateItems(List<ChangeObject> items);
List<ChangeObject> insertItems(List<ChangeObject> items);
} |
BatchService
Splitting of monolythic sync() into three steps is required to support the referential integrity of data: children have to be deleted prior to parents and parents need to be inserted prior to children. And yet we can not addord three remoting calls instead of one (sync()), six calls altogether in our Orders-OrderItems scenario, because server-side transaction can not be spawned across several remoting calls.
That’s where BatchService class from clear.swc comes in play. It refers to sequence of several remote method calls-to-do as a “batch”, or, simply array of BatchMember elements: destination name, method name, array of arguments. Instead of multiple remote calls it sends the “batch” as argument of one remote call – to Java class com.farata.remoting.BatchGateway (daoflex-runtime.jar). In turn, BatchGateway’s method execute(List<BatchMember>) invokes the required remote calls sequentially, wrapping the entire sequence in the JTA begin/commit/rollback.

Here is a snippet illustrating the work of the BatchService:
var bs: com.farata.remoting.BatchService;
. . .
bs = new BatchService();
bs.addEventListener(FaultEvent.FAULT, onFault);
bs.registerCollection(orders, 0); //0 - default (top) priority, parent
bs.registerCollection(orderItems,1); //1 - priority, child of "0"
. . .
var batch:Array = bs.batchRegisteredCollections();
bs.send(batch); |
As you can see, BatchService batch is not confined to DataCollections: you can batch for synchronical server-side execution any sequence of remote calls. While BatchService has been around since 2006. the dataCollection.sync(true) further reduces the amount of code required to transactionally update associated collections. Users of SQL-based branch of the CDB benefit from automatic generation of the required Java functions. Otherwise, your Java DAO has to implement IBatchTransactionServiceSupport.
package collections {
import com.farata.collections.DataCollection;
public class OrderCollection extends DataCollection {
public function OrderCollection(source:Array=null) {
super(source);
destination="com.farata.test.Order";
method="getOrders";
}
}
} |
package collections {
import com.farata.collections.DataCollection;
public class OrderItemCollection extends DataCollection {
public function OrderItemCollection(source:Array=null) {
super(source);
destination="com.farata.test.Order";
method="getOrderItems";
}
}
} |
<?xml version="1.0" encoding="UTF-8"?>
<!-- OrdersPanel.mxml -->
<mx:Panel title="Orders"
xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Metadata>
[Event(name="orderSelectionChange", type="events.OrderEvent")]
</mx:Metadata>
<mx:DataGrid id="dg" dataProvider="{orders}" editable="true" change="onChange()" height="100%" >
<mx:columns>
<mx:DataGridColumn dataField="order_id" headerText="Order Id" />
<mx:DataGridColumn dataField="customer_first_name" headerText="First Name" />
<mx:DataGridColumn dataField="customer_last_name" headerText="Last Name" />
<mx:DataGridColumn dataField="order_date" headerText="Order Date" itemEditor="mx.controls.DateField" editorDataField="selectedDate"/>
</mx:columns>
</mx:DataGrid>
<mx:ControlBar width="100%">
<mx:Button label="Remove" click="onRemove(dg.selectedIndex)" enabled="{dg.selectedIndex != -1}"/>
<mx:Button label="Add" click="onAdd(Math.max(0,dg.selectedIndex+1)); "/>
<mx:Label text="Deleted: {orders.deletedCount}"/>
<mx:Label text="Modified: {orders.modifiedCount}"/>
</mx:ControlBar>
<mx:Script>
<![CDATA[
import events.OrderEvent;
import com.farata.test.dto.OrderDTO;
import com.farata.collections.DataCollection;
[Bindable] public var orders:DataCollection;
private function onAdd(position:int):void {
var item:OrderDTO = new OrderDTO();
item.order_id = "###" + String(orders.length + 1);
item.order_date = new Date();
orders.addItemAt(item, position);
dg.selectedIndex = position;
onChange();
}
private function onRemove(position:int):void {
orders.removeItemAt(position);
}
private function onChange():void {
var event:OrderEvent = new OrderEvent(OrderEvent.ORDER_SELECTION_CHANGE);
event.order = dg.selectedItem as OrderDTO;
dispatchEvent(event);
}
]]>
</mx:Script>
</mx:Panel> |
<?xml version="1.0" encoding="utf-8"?>
<!-- OrderItemsPanel.mxml -->
<mx:Panel title="OrderItems" xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:DataGrid id="dg" dataProvider="{orderItems}" editable="true" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="order_id" headerText="Order Id" />
<mx:DataGridColumn dataField="item_id" headerText="Item Id" />
<mx:DataGridColumn dataField="product_name" headerText="Product" />
<mx:DataGridColumn dataField="quantity" headerText="Quantity" />
</mx:columns>
</mx:DataGrid>
<mx:ControlBar width="100%">
<mx:Button label="Remove" click="onRemove(dg.selectedIndex);" enabled="{dg.selectedIndex != -1}"/>
<mx:Button label="Add" click="onAdd(Math.max(0,dg.selectedIndex + 1)); " enabled="{selectedOrder!=null}"/>
<mx:Label text="Deleted: {orderItems.deletedCount}"/>
<mx:Label text="Modified: {orderItems.modifiedCount}"/>
</mx:ControlBar>
<mx:Script>
<![CDATA[
import com.farata.test.dto.OrderItemDTO;
import com.farata.test.dto.OrderDTO;
import com.farata.collections.DataCollection;
[Bindable]private var orderItems:DataCollection ;
private var _selectedOrder:OrderDTO;
[Bindable]
public function set selectedOrder(order:OrderDTO):void {
_selectedOrder = order;
if (order != null) {
orderItems = order.orderItems;
} else
orderItems = null;
}
public function get selectedOrder():OrderDTO {
return _selectedOrder;
}
private function onAdd(position:int):void {
var item:OrderItemDTO = new OrderItemDTO();
item.order_id = selectedOrder.order_id;
if (item.quantity ==0) item.quantity = 1;
orderItems.addItemAt(item, position);
dg.selectedIndex = position;
}
private function onRemove(position:int):void {
orderItems.removeItemAt(position);
}
]]>
</mx:Script>
</mx:Panel> |
package events{
import com.farata.test.dto.OrderDTO;
import flash.events.Event;
public class OrderEvent extends Event {
public static const ORDER_SELECTION_CHANGE:String ="orderSelectionChange";
public var order:OrderDTO;
public function OrderEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false){
super(type, bubbles, cancelable);
}
}
} |
/* Generated only when absent by ClearDataBuilder according to UserActionScriptDTO.xsl */
package com.farata.test.dto{
import collections.OrderItemCollection;
import com.farata.collections.dto.HierarchicalDTOAdapter;
import com.farata.collections.dto.IHierarchicalDTO;
[RemoteClass(alias="com.farata.test.dto.OrderDTO")]
public class OrderDTO extends _OrderDTO implements IHierarchicalDTO{
//--------------------------------------------//
// Your custom code goes here //
//--------------------------------------------//
[Transient] [Bindable] public var orderItems:OrderItemCollection;
[Transient] public var adapter:HierarchicalDTOAdapter;
public function OrderDTO() {
super();
adapter = new HierarchicalDTOAdapter(this);
orderItems = new OrderItemCollection();
adapter.addCollection(orderItems);
}
public function get childCollections():Array {
return adapter.childCollections;
}
public override function set order_id(orderId:String):void {
if (orderId !== super.order_id) {
super.order_id = orderId;
orderItems.fill(order_id);
}
}
}//OrderDTO
} |
package com.farata.test;
import java.util.List;
/**
* @daoflex:webservice
* pool=jdbc/test
*/
public abstract class Order
{
/**
* @daoflex:sql
* sql=:: select order_id, customer_first_name,
* customer_last_name, order_date from simple_order
* ::
* transferType=OrderDTO[]
* keyColumns=order_id
* updateTable=simple_order
* autoSyncEnabled=true
*/
public abstract List getOrders();
/**
* @daoflex:sql
* sql=select * from simple_order_item WHERE ORDER_ID=:orderId
* transferType=OrderItemDTO[]
* updateTable=simple_order_item
* keyColumns=order_id,item_id,product_name
* autoSyncEnabled=true
*/
public abstract List getOrderItems(String orderId);
} |
CREATE TABLE `simple_order` (
`order_id` char(32) NOT NULL default '',
`customer_first_name` varchar(32) NOT NULL default '',
`customer_last_name` varchar(32) NOT NULL default '',
`order_date` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
INSERT INTO `simple_order` (`order_id`,`customer_first_name`,`customer_last_name`,`order_date`) VALUES
('###1','Victor','Rasputnis','2006-02-04 00:00:00'),
('###2','Yakov','Fain','2006-09-25 00:00:00'),
('###3','Anatole','Tartakovsky','2006-07-01 00:00:00')$$
CREATE TABLE `simple_order_item` (
`order_id` char(32) NOT NULL default '',
`item_id` char(32) NOT NULL default '',
`product_name` varchar(32) NOT NULL default '',
`quantity` int(11) NOT NULL default '1',
PRIMARY KEY (`order_id`,`item_id`,`product_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
INSERT INTO `simple_order_item` (`order_id`,`item_id`,`product_name`,`quantity`) VALUES
('###1','1','Laptop',1),
('###1','2','Battery',1),
('###1','3','Laptop Backpack',1),
('###3','1','Apple',1),
('###3','2','Orange',1) |
Victor Rasputnis
Permalink
|