Introduction

Object Relational Mapper for the haXe programming language.
Generates XML files with SQL queries for flexibility. Parses these XML files into Manager classes.

Download

haxORMap Version 0.71, now with SQLite support, among other things :-)

See also: This project's haxelib page

Usage

You use haxORMap in 2 parts of your application. By implementing the Haxormap interface, the tool can recognize which classes you want to use as data models. The special types included in the data.model.type.* package, define certain properties.

The haxORMap parsetool makes extensive use of the XML documentation generated by the haXe compiler. For this reason, your application will have to be compiled at least twice. First with the -xmldoc xmlfile.xml -D haxormap options, to generate XML documentation which the haxORMapper needs.

Example:

import data.model.type.SmartTypes;

#if haxormap
typedef WhatEverIWantToCallMyManager = data.model.Haxormanager<BasicDatabaseTable, Dynamic>

// We need to use a typedef here, because the "WhatEverIWantToCallMyManager" does not exist yet.
// It will exist as soon as it's generated by haxormap.
// Before haxormap can generate this class, we need the XML documentation generated by the haXe compiler.
// So this typedef is only used to shut up the compiler ;-)
#end

class BasicDatabaseTable implements data.model.Haxormap
{
	var primarykey : Primary<Int>;
	var regularField : String;
	
	var fieldNotInTable : Private<String>;
	
	static public var sql = new WhatEverIWantToCallMyManager();
}

The -D haxormap option is used by you and the data.model.Haxormanager class. It is actually a hack, which could maybe be circumvented in another way, ideas, anyone?.
For more information about the SmartTypes, consult the documentation here.

After writing your class(es), you can invoke the haxormapper. So for this example we run the following commands:

	haxe BasicDatabaseTable -neko test.n -xml test.xml -D haxormap
	haxormap --xmldoc test.xml --generate-sql --generate-classes
			

This will give you 3 files: WhatEverIWantToCallMyManager.hx, SQLXML/WhatEverIWantToCallMyManager.xml.sql and SQLXML/MySQL.create.sql (SQLite and PostgreSQL support is planned).

-- Snip [WhatEverIWantToCallMyManager.xml.sql] --

<exec methodName="save"><![CDATA[
	UPDATE `basicdatabasetable`
	   SET regularfield=::regularField::
	 WHERE primarykey=::primarykey::
]]></exec>

<set methodName="select"><![CDATA[
	SELECT b.primarykey, b.regularfield
	  FROM `basicdatabasetable` AS b
	 WHERE $$o(b.primarykey=::primarykey::)
]]></set>

<row methodName="get" dbms="SQLite"><![CDATA[
	SELECT b.primarykey, b.regularfield
	  FROM `basicdatabasetable` AS b
	 WHERE b.primarykey=::primarykey::
	 LIMIT 1
]]></row>
			

By using the 'dbms' attribute, you can write DBMS specific queries.
All ::templatevariables:: will be escaped by its type defined in the BasicDatabaseTable class.
The $$o() template macro makes a template variable optional.

The methodName attribute is used when generating the Manager class. You can add your own queries in the XML file. I think it speaks for itself what will happen ;-)

Advanced example

What makes haxORMap unique, are it's relation capabilities. Let's take the following code for example:

By the way, every class manager in the example is linked to the code that would be generated, so you can see the results on this webpage. Every class is linked to the generated XML file.

class Client implements data.model.Haxormap
{
	var id : Serial;
	var address : Varchar<200>;
	var name : String;
	
	public static var sql = new Clients();
}

class Order implements data.model.Haxormap
{
	var id : Serial;
	var client : Primary<Client>;
	var referrer : Client;
	var date : Date;
	public static var sql = new Orders();
}

class Orderline implements data.model.Haxormap
{
	var id : Serial;
	var order : Primary<Order>;
	var number : Int;
	var product : String;
	public static var sql = new Orderlines();
}

/**
	Some more sauce in here: 
    <sql-options>
	<table prefix="my" name="orderdelivery" alias="d" notNull="amount" />
	<onDelete disallow="orderline" />
	<onUpdate disallow="orderline" />
	<rename orderlineid="lineid" />
    </sql-options>
**/
class OrderlineDelivery implements data.model.Haxormap
{
	var orderline : Primary<Orderline>;
	var address : Primary<String>;
	var amount : Int;
	public static var sql = new OrderlineDeliveries();
}

Generated MySQL CREATE TABLE statements for this example
Generated SQLite CREATE TABLE statements for this example

See also OptionNode and KeyAction.

What <sql-options> does (yes you put it inside haxedoc comments for the class) is:
1. Set table name to myorderdelivery (prefix is seperated to autogenerate better aliases and for future purposes)
2. Use the alias d in queries: SELECT column AS _d_column
3. The column amount may not be NULL.

Furthermore:
4. Whenever the Foreign key defined by class variable orderline tries to delete a row which has OrderlineDelivery rows associated, disallow the delete.
5. Same for updating.
6. Rename the (generated)columnname orderlineid to lineid
The rename field was done to make it easier to interface with exisiting database tables.

What you might notice here, is that haxORMap recognizes class variables that point to other Haxormaps.

Futhermore Order has 2 references to the Client class. This is also handled as you can see down here:

<row methodName="get"><![CDATA[
	SELECT o.id, o.clientid AS _c_id, o.date, o.referrer_clientid AS _c0_id, 
	       c.address AS _c_address, c.name AS _c_name, c0.address AS _c0_address, c0.name AS _c0_name
	  FROM `order` AS o
	  LEFT JOIN `client` AS c ON o.clientid=c.id
	  LEFT JOIN `client` AS c0 ON o.referrer_clientid=c0.id
	 WHERE o.id=::id:: AND c.clientid=::client.id::
	 LIMIT 1
]]></row>

The joined data gets split up into their respective objects:

var x = Orders.instance.get(/** id **/0, /** clientId **/ 0);
// Allmost the same as:
var x = Order.sql.get(0,0);
trace('Order was placed by: '+x.client.name);

Roadmap / Todo

Contact

BlueZeniX @ freenode IRC @ #haxe
Or mail the haXe mailing list.