Saturday, October 30, 2010

Gorm Performance of collections - continued

In my last post - I told you about the issue Burt Beckwith brought up at the SpringOne conference with adding a member to a 1-to-many relationship. That issue is that hibernate will load all of the values from the many side to make sure the new item can be added. If this number is small, then that is fine - but if there are a lot then you have a potential issue.

Burt also talked about a solution. His suggested solution reminded me a little of the old joke:

Patient: Doctor, Doctor - it hurts when I do this
Doctor: Then don't do that

Burt's suggestion was to not model Libary to Visits as a 1-to-many relationship. Instead remove the collection altogether from the Library and give the Visit a reference back to the library.

Now the Library and the Visit look like the following:
Library:
1:    
2: class Library {
3: String name
4:
5: static constraints = {
6: }
7: }
8:


Visit:
1:  class Visit {  
2: String personName
3: Date visitDate = new Date()
4:
5: Library library
6: static constraints = {
7: }
8: }
9:


Notice now that Library has no reference at all to a Visit, but Visit still has a reference to the Library.

Because of this change, it will change our usage of these domain classes - but interestingly enough it does not change the table definition. Lets look at the tables for the original Library and the new Library:

1:  create table library (  
2: id bigint generated by default as identity (start with 1),
3: version bigint not null,
4: name varchar(255) not null,
5: primary key (id));
6:
7: create table library (
8: id bigint generated by default as identity (start with 1),
9: version bigint not null,
10: name varchar(255) not null,
11: primary key (id));
12:
13:


You can see from above that the table definition is exactly the same so there will be no impact at the database level - but there is an impact at the code level.

Now, instead of adding Visits to a library we have to pass a reference to the library to the Visit. Something like the following:
1:    Library library = new Library(name:'LocalLibrary3')  
2: int maxVisits = 100
3: library.save()
4:
5: (1..maxVisits).each {
6: new Visit(personName:"person${it}", library:library).save()
7: }
8:


Notice above we create an instance of the Library, then as we create Visits we add a reference to the library that the visit is associated with. There is no dynamic method on the Library class called 'addToVisits' - because there are no visits.

Lets look at the entire integration test. Note that I changed the name of the Library class to Library3 and Visit3 so they did not conflict with the original classes.

1:   void testAddingLibrary2Visits3() {  
2: Library3 library3 = new Library3(name:'LocalLibrary3')
3: int maxVisits = 100
4: library3.save()
5:
6: (1..maxVisits).each {
7: new Visit3(personName:"person${it}", library:library3).save()
8: }
9:
10: sessionFactory.getCurrentSession().flush()
11: sessionFactory.getCurrentSession().clear()
12:
13: assertEquals 1, Library3.count()
14: assertEquals maxVisits, Visit3.count()
15:
16: sessionFactory.getCurrentSession().flush()
17: sessionFactory.getCurrentSession().clear()
18:
19: println "============= Find the library ============="
20:
21: Library3 library33 = Library3.findById(library3.id)
22:
23: println "============= adding one more visit ============="
24: Visit3 oneMoreVisit = new Visit3(personName:"oneMorePerson",library:library33)
25: oneMoreVisit.save()
26: println "============= saved new Visit one more visit ============="
27: assertEquals maxVisits+1, Visit3.countByLibrary(library33)
28:
29: sessionFactory.getCurrentSession().flush()
30: sessionFactory.getCurrentSession().clear()
31: println "============= Done adding one more visit ============="
32:
33: println "============= Check count of libraries and visits ============="
34: assertEquals 1, Library3.count()
35: assertEquals maxVisits+1, Visit3.count()
36: }
37:


Lets look at each section of the unit test and the result sql statements:
1:  INTEGRATIONTEST  
2: println "============= Find the library ============="
3: Library3 library33 = Library3.findById(library3.id)
4:
5: SQL
6: ============= Find the library =============
7: Hibernate:
8: select
9: this_.id as id9_0_,
10: this_.version as version9_0_,
11: this_.name as name9_0_
12: from
13: library3 this_
14: where
15: this_.id=?
16:

On line 3 above, we perform a find for the library instance. You can see this results in a single select, very much like the last time.

1:  INTEGRATION TEST  
2: println "============= adding one more visit ============="
3: Visit3 oneMoreVisit = new Visit3(personName:"oneMorePerson",library:library33)
4: oneMoreVisit.save()
5:
6: SQL
7: ============= adding one more visit =============
8: Hibernate:
9: insert
10: into
11: visit3
12: (id, version, library_id, person_name, visit_date)
13: values
14: (null, ?, ?, ?, ?)
15:

This is where we really see the benefit. On lines 3, we create the new Visit and pass a reference to the library. On line 4, we save the new visit. Notice the resulting sql does not make a select of all of the visits. Instead it can insert the record directly into the table. This clearly can be a huge performance gain if your collections are very large.

This is something you need to think about when designing your model. GORM can sometimes appear to be magic. I was told once that if it appears to be magic it is because you do not understand it. If you dont understand it, then subtle cases like this will cause you problems on a project.

If you know about this ahead of time, then you can implement it the correct way. But sometimes, it is not clear if you have to do this and you never want to make premature optimizations before you know you have to.

I was wondering then, what would happen if you implemented the collection in the conventional way - and then later had to change it. The usage is significantly different that it would have a ripple effect in you code. I wanted to see if I could retrofit the existing api, with the above implementation.

I got close - but I could not get it to match completely. I got very close, but I needed to add an additional flush:true in the original integration test.

Below I show my attempt to modify the Library class to accommodate the above usage. I called the new classes Library2 and Visit2.

Library2
1:  class Library2 {  
2:
3: String name
4: Boolean visitsDirty = false
5:
6: private List<Visit2> _visits = new ArrayList<Visit2>()
7:
8: Library2 addToVisits(Visit2 theVisit) {
9: if (theVisit) {
10: theVisit.library = this
11: _visits << theVisit
12: visitsDirty = true
13: }
14: return this
15: }
16:
17: List<Visit2> getVisits() {
18: List<Visit2> theVisits = Visit2.findAllByLibrary(this)
19: }
20:
21: def afterSave = {
22: Library2 lib2 = Library2.load(this.id)
23: _visits.each {
24: it.library = lib2
25: it.save()
26: }
27: _visits.clear()
28: visitsDirty = false
29: }
30: def afterInsert = {
31: Library2 lib2 = Library2.load(this.id)
32: _visits.each {
33: it.library = lib2
34: it.save()
35: }
36: _visits.clear()
37: visitsDirty = false
38: }
39: def afterUpdate = {
40: Library2 lib2 = Library2.load(this.id)
41: _visits.each {
42: it.library = lib2
43: it.save()
44: }
45: _visits.clear()
46: visitsDirty = false
47: }
48: def beforeDelete = {
49: Visit.executeUpdate("delete Visit2 v where v.library = ?", [this])
50: }
51:
52: static constraints = {
53: }
54: }
55:


as you can see I had to hook into a number of Hibernate events.

My conclusion after the exercise above was that it was better to re-write the implementation than it was to try to retrofit the api.

I hope you found this useful and will be better prepared to design and implement your collections with Grails and GORM.

Friday, October 22, 2010

SpringOne 2GX Conf - GORM Performance

I was lucky enough to attend the SpringOne 2GX conference held in the Chicago area this week. There were plenty of great sessions - and they have given me much information to blog about.

This blog post is inspired by a session by Burt Beckwith who has an awesome blog himself. Check it out!

Burt's session was called 'Advanced GORM - Performance, Customization and Monitoring'. In this session he showed an interesting 'gotcha' which I looked into further and thought others might like to be made aware of this one. I would like to say I knew this at some point - but I have to admit I had forgotten this potential issue.

Two caveats:
1) This issue is really a result of what hibernate has to do to make the domain model correct. It is just that GORM makes it easy to fall into this issue - it is not a GORM issue. It is also not really a hibernate issue either - it just is.
2) Burt stressed that what he showed was to inform, not to bias people away from a particular modeling style.

The issue is centered around Grails 'One-to-Many' relationships. Burt used Library and Visit domain objects in his example - and if it is not too egregious - I will use the same in this blog post.

Imagine we have to keep track of the number of visits to a particular library. The standard modeling technique would be a single Library has many Visits. This is very easy to model in GORM ( and that ease in a way contributes to the issue ).

This can be modeled like the the following:
1:  class Library {  
2: String name
3: static hasMany = [visits:Visit]
4:
5: }
6:


Where a Visit might look like the following:
1:  class Visit {  
2: String personName
3: Date visitDate = new Date()
4: static belongsTo = [library:Library]
5: }
6:


So far so good, or is it... what is the problem?

For the hasMany relationship a hibernate Persistent Set is created by default. A Set requires all of the elements in the Set be unique. How would hibernate know if the new item is unique - without loading all of the elements to verify that it is unique? The short answer is, it has to load all of the elements first. Yes, all visits to the library - all of them. This could number into the millions at some point. Even if as a result of business rules we know that the Visit is unique.

In hibernate, the relationship could have been modeled as a bag - but unfortunately GORM does not support a bag.

Lets look at this and see if that is true. Below is a unit test which demonstrates this.
1:    void testAddingLibraryVisits() {  
2: Library library = new Library(name:'LocalLibrary')
3: int maxVisits = 100
4:
5: (1..maxVisits).each {
6: library.addToVisits(new Visit(personName:"person${it}"))
7: }
8: library.save()
9: sessionFactory.getCurrentSession().flush()
10: sessionFactory.getCurrentSession().clear()
11:
12: assertEquals 1, Library.count()
13: assertEquals maxVisits, Visit.count()
14:
15: sessionFactory.getCurrentSession().flush()
16: sessionFactory.getCurrentSession().clear()
17:
18: println "============= Find the library ============="
19:
20: Library newLibrary = Library.findById(library.id)
21:
22: println "============= Create one more visit ============="
23: Visit oneMoreVisit = new Visit(personName:"oneMorePerson")
24: println "============= Add and Save one more visit ============="
25: newLibrary.addToVisits(oneMoreVisit).save()
26: println "============= Check number of visits in library ============="
27: assertEquals maxVisits+1, newLibrary.visits.size()
28:
29: sessionFactory.getCurrentSession().flush()
30: sessionFactory.getCurrentSession().clear()
31: println "============= Done adding one more visit ============="
32:
33: println "============= Check count of libraries and visits ============="
34: assertEquals 1, Library.count()
35: assertEquals maxVisits+1, Visit.count()
36: }
37:


Lets look at each section.

The first part, lines 1-17 just get data setup. It is the other lines that are interesting.

Lines18-20 which are summarized below with their SQL output.

1:  println "============= Find the library ============="  
2: Library newLibrary = Library.findById(library.id)
3:
4: ============= Find the library =============
5: Hibernate:
6: select
7: this_.id as id8_0_,
8: this_.version as version8_0_,
9: this_.name as name8_0_
10: from
11: library this_
12: where
13: this_.id=?
14:
15:

After saving a 100 visits to the library, we get setup to add one more visit. In line 2 above, we perform a 'findById' on the library. You can see from the resulting query that the visits have not been loaded because by default all collections are lazy.

Next we add a newly created visit to the collection.
1:  println "============= Create one more visit ============="  
2: Visit oneMoreVisit = new Visit(personName:"oneMorePerson")
3: println "============= Add and Save one more visit ============="
4: newLibrary.addToVisits(oneMoreVisit).save()
5:
6: ============= Create one more visit =============
7: ============= Add and Save one more visit =============
8: Hibernate:
9: select
10: visits0_.library_id as library3_1_,
11: visits0_.id as id1_,
12: visits0_.id as id2_0_,
13: visits0_.version as version2_0_,
14: visits0_.library_id as library3_2_0_,
15: visits0_.person_name as person4_2_0_,
16: visits0_.visit_date as visit5_2_0_
17: from
18: visit visits0_
19: where
20: visits0_.library_id=?
21: Hibernate:
22: insert
23: into
24: visit
25: (id, version, library_id, person_name, visit_date)
26: values
27: (null, ?, ?, ?, ?)
28: Hibernate:
29: call identity()
30: Hibernate:
31: update
32: library
33: set
34: version=?,
35: name=?
36: where
37: id=?
38: and version=?
39: ============= Done adding one more visit =============
40:



In line 2 we create a new Visit instance, and in line4 we add this to the visits collection and save the library.

Notice starting in line 8 - that we are loading all of the visits for the particular library. This is where the issue comes into play.
On line 21 the new visit record is being inserted into the visit table and finally on line 31 the Library instance is saved.

I hope you can see that this could definitely be a performance issue. It will work great in dev, and great in QA - but 1 month into production and this will be an issue.

This concludes the database interaction to save one more item to a collection.

In my next blog post I will discuss what Burt suggested to address this problem, and a set of API changes to implement Burt's suggestion while keeping the unit test intact.

Thanks for stopping by and stop by again to see the next blog post.

Wednesday, October 13, 2010

Using the H2 Database CSVREAD with a sequence ID Column

The problem I was researching was the following:
Make the contents of a csv file, a potentially very large csv file, available to a Grails web application and be able to query against the fields as though it were a database.

I had a need to read a csv file and put this into a database so I could later query against it and the database had to persist across web application restarts. My research ( which amounted to a bunch of google searches ) led me to the database H2. You can find out more about H2 here.

More research led me to a pretty cool google code project called, gcsvsql which was a project that contained a groovy script to use H2, read the file(s) and put the data into an in memory database to query against on the command line or in a groovy shell.

This was almost exactly what I needed. I just needed to get this into a file based database because the data had to persist across restarts of the web application, and I really wanted an ID column. The data had a business ID column, that was probably unique - but since I did not control it I really wanted a surrogate ID column. Plus I originally thought I would use GORM and having an ID column just makes GORM life easier.

What I ended up doing was to use much of the gcsvsql script, added some additional parameters for output database directory, table name to use, etc. The important 5 lines are below.

1:  def sql = Sql.newInstance("jdbc:h2:file:${outputDir}/${tableName}","org.h2.Driver")  
2:
3: def seqstmt = "create sequence " + tableNameSeq + " start with 1"
4:
5: def stmt = "create table $tableName(ID INT PRIMARY KEY, $columnTypes) as select (select " + tableNameSeq + ".nextval from dual), * from csvread('$pathToCsvFile')" as String
6:
7: sql.execute(seqstmt)
8:
9: sql.execute(stmt)
10:


In Line 1, I am just using Groovy Sql to establish a connection a database based on some input parameters.
in Line 3, I am create a sequence that I am going to use to hold the primary key ID column values
In Line 5, I am creating a new table in the database, from the input parameters using the sequence to generate the primary key ID field.

Lines 7 and 9 just get the ball rolling after the setup.

Why this blog posting? Well - I never actually found a blog with this exact example of reading a csvfile into a file based H2 database and adding a new ID column based on a dynamically created sequence so I wanted to share back. As I mentioned, I borrowed heavily from the gcsvsql script, so you should definitely check that out.

If you want to see the entire script - leave me a comment and I will add the file to a future blog post along with my Groovy Sql reading of the data.

I hope this was useful to someone out there.

Sunday, September 5, 2010

Groovy Categories for temporary mixins

In my previous post I showed how you could add the Apache StringUtils capability into the standard String class in BootStrap.groovy for the entire application to use.

But what if for some reason you do not have access to BootStrap.groovy or adding this kind of mixin for everyone is a little egregious.

This is where Groovy Categories come in very handy. A Groovy 'Category' can be added to any class at runtime by using the 'use' Groovy keyword.

Below is a simple unit test which shows how this is done very easly.

Lines 2-6 show the typical Java usage( with a little Groovy thrown in) using StringUtils.

Lines 9 - 13 so the how the 'use' keyword can be used to temporarily mixin the StringUtils capabilities to a String.


1:    void testUseCategory() {  
2: String testString = "break.this.string.up.into.words"
3: println "Test StringUtils.split"
4: StringUtils.split(testString, ".").each {
5: println it
6: }
7:
8: println "Test use(StringUtils)"
9: use(StringUtils) {
10: testString.split(".").each {
11: println it
12: }
13: }
14: }
15:


This example shows how to take a third party library and mix it in, but you can also do this for your own utility classes. You could create a utility class that operates on your class, and then 'use' that utility class to mix those methods in as though they exist on your class. This helps to keep your class focused on the business problem and makes the class much easier to read.

Just another example of some really cool Groovy capability.

I don't always code - but when I do, I prefer Groovy.

Sunday, August 22, 2010

Adding StringUtils to the String Class in Groovy/Grails

This one has been blogged about before - but I thought it was just such an easy way to add very useful functionality to the String class that I thought I would call it out.

I had the need to use the Apache StringUtils methods so often in my application that I started to wish that the methods were just native to the String class. Adding this kind of capability is called mixins in Groovy.

To check if a string is null, empty or whitespace, with the Apache package you have to write:

If( StringUtils.isBlank( myStringToCheck) ) {
// then is blank
}

what I wanted to do was:

if( myStringToCheck.isBlank() ) {
// then is blank
}

I just thought this was easier to read and easier to write.

This is actually incredibly easy to do in Groovy/Grails.

The way I went about do this was to add the following line of code to the Bootstrap.groovy file:

// add apache commons lang StringUtils methods to the String class
String.metaClass.mixin StringUtils

add the commons-lang jar file to the lib directory and that is it.

Now for any String I can use any of the methods in the StringUtils package as though they were native methods to the String class.

Nice!

Wednesday, May 5, 2010

Grails: Dynamically adding a save method to Domain objects that show errors

I was writing some integration tests and I noticed that one of my asserts was failing on a save. It was not clear to me why, so I started to add some diagnostic printing of errors. Then I thought - what happens if another domain object has a problem saving? Am I going to have to do this kind of error check all of the time? Ugh..

Using what I have already blogged about in terms of adding metaprogramming for Services, and a quick google search: See this link where I got most of the inspiration.

What I did was add the following method to Bootstrap.groovy.

  def addDebugSaveToDomainObjects() {
grailsApplication.domainClasses.each {
it.metaClass.debugSave = {
def obj = delegate.save()
if( obj == null ) {
println "Error saving " + delegate.getClass().name
delegate.errors.allErrors.each {
println it
}
}
return obj
}
}
}



The above code will add a new method to all Domain objects call debugSave. If there is a problem while saving the domain object this new method will print all of the errors. These errors can be seen in the junit reports.

I did it this way because I really only want these methods when I am testing and I did not want to clutter the domain API with this debug code.

In the bootstrap, I look at the environment and based on the environment name I will call the above closure ( or not as the case may be ).

Next in my integration test, I can execute code like the following:

def myDomain = new Domain(....)
assertNotNull myDomain.debugSave()

If the assert fails, then the Junit reports will tell me so and the output will tell me why.

I hope you found this information useful.

Tuesday, March 30, 2010

Grails: Intercepting Service class methods - part 3

Well - I have a couple of updates to my previous post:

1) Grails 1.2.2 is out and it does look like the Spring DSL for AOP definitions is fixed.
2) using the Spring xml definition in part 2 performed very poorly. I am not sure why but if I changed the pointcut definition to:

pointcut="execution(* com.redpointtech.flex..*Service..*(..))"


it performed MUCH better

Given that the DSL is fixed and I needed it to perform better I have changed the AOP definition again to the following in the resources.groovy file.

 beans = {  
xmlns aop:"http://www.springframework.org/schema/aop"
securityAspect(SecurityAspect)
aop {
config("proxy-target-class":true) {
aspect(id:'theSecurityAspectDef', ref:'securityAspect') {
around method: "invoke", pointcut:"execution(* com.redpointtech.flex..*Service..*(..))"
}
}
}
}


I think this concludes my saga to get Flex services intercepted. I hope this helps you out.

Tuesday, March 16, 2010

Grails: Intercepting Service class methods - part 2

As a continuation from my earlier post about intercepting Grails Service methods - as it turns out that approach will not work when using flex-remoting. If you want to intercept your services that you expose as Flex services you have to use Spring AOP. Because of a bug: http://jira.codehaus.org/browse/GRAILS-5932 you will have to use the XML version of AOP definition.

To do that you have to create a resources.xml file in grails-app/conf/spring directory and it would look like the following:

 <?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--
we are defining the AOP beans in the xml file because of this bug which will
not be fixed until 1.2.2 and we are using 1.2.1
http://jira.codehaus.org/browse/GRAILS-5932
aspect are called twice because of the above bug. See
http://n4.nabble.com/Advice-called-2-or-more-times-for-aspect-with-execution-pointcut-Bug-td1588831.html
toward the bottom Graeme says that 1.2.2 will have the fixes, and 1.3 M1 already does have the
fix. For now we have to live with the interceptor called twice.
-->
<bean id="myAroundAspect" class="com.redpointtech.aop.MyAroundAspect" />
<aop:config proxy-target-class="true">
<aop:aspect id="theAspect" ref="myAroundAspect">
<aop:around method="invoke" pointcut="target(com.redpointtech.aop.AOPMarkerInterface)" />
</aop:aspect>
</aop:config>
</beans>


This assumes that you want to intercept any class that implements the AOPMarkerInterface.

The Aspect looks like the following:

 class MyAroundAspect {  
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnVal = null
println "MyAroundAspect called...."
returnVal = joinPoint.proceed()
return returnVal
}
}


A couple of take aways:
1) do not try to do what I suggested in my previous blog post. That will NOT work for Flex services
2) do not use resources.groovy and the Spring DSL - there is a bug in the 1.2.x version that prevents that from working. This is suppose to be fixed in the 1.2.2 release ( not yet released) and the 1.3 M1 ( available now )
3) do use resources.xml and define your Aspect the old fashion way - the old ways work just great.

I hope this saves you some time.

Saturday, March 13, 2010

Grails: Intercepting Service class methods

It took me a little while to fully understand how to add some kind of interceptor to a Grails Service class and I wanted to add a blog post incase others have a similar question. After doing some research, and piecing together information from various sites I ended up with the information here. I hope it is helpful to you.

What problem am I trying to solve?
Adding interceptors to Controllers is really easy and I wanted to add some interceptors to my Service classes in a similar fashion. But it appears that you cannot do this for Service classes - just Controller classes. If I am wrong, please someone show me how best to do this.

Why am I trying to do this?
I use Flex in many of my application implementations and I use BlazeDS/AMF in many of the cases. I needed to add a security check on every exposed Flex service.

How did I do this?
In my service class I added a custom property that I called 'secureService'. But as you will see you can use anything.

 class TeslaService {  
boolean transactional = true
// secureService is a custom property that is looked for in the BootStrap.groovy file and if
// found in a ServiceClass, then it modifies the invokeMethod to add some code before and
// after the call.
boolean secureService = true
def serviceMethod() {
println 'Tesla Service Method called'
}
}


Next I updated my BootStrap.groovy file and for each ServiceClass, checked for the 'secureService' property being set to true. If it was, I then used the MetaClass InvokeMethod and assigned a new closure that could add a security check.

The updated bootstrap file looked like the following:
 class BootStrap {  
def grailsApplication
def init = { servletContext ->
grailsApplication.serviceClasses.each {
def isSecured = it.getPropertyValue('secureService')
if( isSecured ) {
// example of how to 'intercept' service classes.
it.metaClass.invokeMethod = { name, args ->
println '''SECURE before $name'''
def res = delegate.metaClass.getMetaMethod(name,args).invoke( delegate, args)
println '''SECURE after $name. res=$res'''
res
}
}
}
}
def destroy = {
}
}


To test this I created an Integration test. This test looked like the following:

 class TeslaServiceIntegrationTests extends GrailsUnitTestCase {  
def teslaService
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testServiceMethod() {
teslaService.serviceMethod()
}
}


The output of this was the following:
SECURE before serviceMethod
Tesla Service Method called
SECURE after serviceMethod. res=null

Some of the links that I used are below as the information from these posts might also be helpful to you.

http://www.pubbs.net/grails/201001/9868/
http://mrhaki.blogspot.com/2010/01/grails-goodness-access-grails.html
http://www.grails.org/doc/latest/api/org/codehaus/groovy/grails/commons/ClassPropertyFetcher.html

Tuesday, January 5, 2010

FlexBuilder 3 and FlexUnit4 RC1

This will be a quick post on how to setup FlexBuilder project to use FlexUnit4. This post will be more prescriptive with just the steps without a lot of explanation. The FlexUnit site has good documentation.

1) Setup a Flex Project as you normally would.
2) Download the FlexUnit4 from this link.
3) Unzip and copy all of the swc files to your projects libs directory. There should be 4 swc files.
4) create a package/folder under the src directory called flexUnitTests. It could be called anything.
5) Create a test Suite class. This is just a POAO ( plain old actionscript object ). I called mine FlexUnit4Suite and it looked like the following:
1:  package flexUnitTests  
2: {
3: import org.flexunit.runners.Suite;
4: [Suite]
5: [RunWith("org.flexunit.runners.Suite")]
6: public class FlexUnit4Suite
7: {
8: public var testCase1:MyTestCase;
9: }
10: }


Lines 4 and 5 are the most interesting. Use the Suite and RunsWith metadata tags. Line 8 is a reference to your TestCase class. As you add more TestCase classes, just add a new variable here. You dont have to instantiate it, or anything - just declare it.

6) Create your TestCase class, which again is a POAO. Mine looked like the following:

1:  package flexUnitTests  
2: {
3: import flexunit.framework.Assert;
4: public class MyTestCase
5: {
6: public function MyTestCase()
7: {
8: }
9: [BeforeClass]
10: public static function runBeforeClass():void {
11: trace("runBeforeClass");
12: }
13: [AfterClass]
14: public static function runAfterClass():void {
15: trace("runAfterClass");
16: }
17: [Before]
18: public function runBeforeEveryTest():void
19: {
20: trace("runBeforeEveryTest");
21: }
22: [After]
23: public function runAfterEveryTest():void
24: {
25: trace("runAfterEveryTest");
26: }
27: [Test]
28: public function testTrue():void
29: {
30: trace("checkMethod");
31: Assert.assertTrue( true );
32: }
33: [Ignore("Not Ready to Run")]
34: [Test]
35: public function testNotReady():void
36: {
37: Assert.assertFalse( true );
38: }
39: }
40: }


7) Create a new Application for the test runner. You dont have to create a new application, but I assume you already have an Application class that really runs your application. I created another Application called FlexUnitTestRunner. It looked like the following:

1:  <?xml version="1.0" encoding="utf-8"?>  
2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
3: xmlns:flexunitrunning="http://www.adobe.com/2009/flexUnitUIRunner"
4: minWidth="1024" minHeight="768" creationComplete="onCreationComplete()">
5: <mx:Script>
6: <![CDATA[
7: import flexUnitTests.FlexUnit4Suite;
8: import org.flexunit.listeners.UIListener;
9: import org.flexunit.runner.FlexUnitCore;
10: private var flexUnitCore:FlexUnitCore;
11: private function onCreationComplete():void {
12: flexUnitCore = new FlexUnitCore();
13: flexUnitCore.addListener(new UIListener( testRunner));
14: flexUnitCore.run(FlexUnit4Suite);
15: }
16: ]]>
17: </mx:Script>
18: <flexunitrunning:TestRunnerBase id="testRunner" width="100%" height="100%" />
19: </mx:Application>



Run the FlexUnitTestRunner application and my console output looked like:
1:  runBeforeClass  
2: runBeforeEveryTest
3: checkMethod
4: runAfterEveryTest
5: runAfterClass


and a browser window popped up with the results of my test case.

Thats it in a nutshell how to get a Flex Builder 3, sdk 3.5, FlexUnit4 up and running quickly.

Sunday, January 3, 2010

FlexORM

What seems like many years ago now, because it was - I saw a presentation by Christophe Coenraets where he talked about a simple ORM for AIR. You can see the original blog post here.

I have used Hibernate for many years, and now most recently GORM and I am a big fan of ORM tools when used right and in the right measure.

When I started looking at an AIR application I decided to revisit the work done in that original blog posting and I was very pleased to see that work had continued on with the ORM mapping, now called FlexORM. (Which is an interesting name since it only works with AIR ). You can find more information about the FlexORM tool here.

What I am going to do is present some examples of how I used it. I am hoping that my examples will help answer your questions on how to use the tool or inspire you start using it.

In my simple application my domain model is one where a Project can have sub-projects to form a project hierarchy and each project can have many TimeEntry references but a TimeEntry is only allocated to a single Project.

Lets look at the Project domain class first.
package com.redpointtech.domain
{
import mx.collections.ArrayCollection;
import mx.collections.IList;

[Bindable]
[Table( name="PROJECT")]
public class Project
{
// each project can contain a number of child or
// sub projects.
private var _subProjects:IList = new ArrayCollection();
private var _timeEntries:IList = new ArrayCollection();

[Id]
public var id:int;

[Column( name="proj_name")]
public var name:String;

[Column( name="proj_desc")]
public var desc:String;

[Column(name="color")]
public var color:Number;

[ManyToOne(name="parent_id", inverse="true")]
public var parent:Project;

[OneToMany(type="com.redpointtech.domain.Project", fkColumn="parent_id", lazy="false", cascade="save-update", indexed="true")]
public function set subProjects(value:IList):void {
_subProjects = value;
}
public function get subProjects():IList {
return _subProjects;
}
public function addSubProject(value:Project):void {
value.parent = this;
_subProjects.addItem(value);
}

[OneToMany(type="com.redpointtech.domain.TimeEntry", fkColumn="project_id", lazy="true", cascade="all", indexed="true")]
public function set timeEntries(value:IList):void {
_timeEntries = value;
}
public function get timeEntries():IList {
return _timeEntries;
}
public function addTimeEntry(value:TimeEntry):void {
value.project = this;
_timeEntries.addItem(value);
}


public function Project()
{
}

}
}


The primary metadata tags I used user:
[Table( name="tbd")] to define the table name for the domain object.

[Id] to tell the ORM which field is used for an id. As of right now the id value must be of type 'int'.

[Column( name="tbd")] to define the column name for a property. If the name field is not specified then it uses the property name.

[ManyToOne(name="col name", inverse="true/false")] to define the ManyToOne relationship with itself.

[OneToMany(...)] metadata tag is specific on the set/get methods. Also noticed that we added an 'add' method which sets the parent of the Project and adds the Project to the collection of sub projects.

The TimeEntry class looks like the following:
package com.redpointtech.domain
{
import com.redpointtech.util.Constants;

[Bindable]
[Table( name="TIMEENTRY")]
public class TimeEntry
{

[Id]
public var id:int;

[Column( name="full_year")]
public var fullYear:int;

[Column( name="month")]
public var month:int;

[Column( name="day_of_month")]
public var dayOfMonth:int; // Date.date equivalent

[Column( name="start_hour")]
public var startHour:int;

[Column( name="start_min")]
public var startMin:int;

[Column( name="end_hour")]
public var endHour:int;

[Column( name="end_min")]
public var endMin:int;

[Column( name="notes")]
public var notes:String="";

[Column( name="summary")]
public var summary:String="";

[ManyToOne(name="project_id", inverse="true")]
public var project:Project;

public function TimeEntry()
{
}

public function setDate(date:Date):void {
fullYear = date.fullYear;
month = date.month;
dayOfMonth = date.date;

}
public function setStartTime( hour:int, min:int):void {
startHour = hour;
startMin = min;
}
public function getStartDate():Date {
var startDate:Date = new Date(fullYear, month,dayOfMonth,startHour,startMin);
return startDate;
}

public function setEndTime(hour:int, min:int):void {
endHour = hour;
endMin = min;
}
public function getEndDate():Date {
var endDate:Date = new Date(fullYear,month,dayOfMonth,endHour,endMin);
return endDate;

}
private function _getTimeDiffInMillis():Number {
var startDate:Date = getStartDate();

var endDate:Date = getEndDate();

var diff:Number = endDate.getTime() - startDate.getTime();

return diff;
}

[Transient]
public function get elapsedHours():Number {
var diff:Number = _getTimeDiffInMillis();

var hours:Number = diff / Constants.millisecondsPerHour;

var floorHours:Number = Math.floor(hours);


return floorHours;
}

[Transient]
public function get elapsedMinutes():Number {
var hours:Number = elapsedHours;
var milliHours:Number = hours * Constants.millisecondsPerHour;

var diff:Number = _getTimeDiffInMillis();

var minuteDiff:Number = diff - ( milliHours );

var minutes:Number = minuteDiff / Constants.millisecondsPerMinute;
var roundedMinutes:Number = Math.min(Math.round(minutes),59);
return roundedMinutes;

}

}
}


The only real difference is the use of Transient to tell FlexORM that these are not used in the persistence of the object.

Now that we have a feel for how to annotate our domain classes to be used by FlexORM, lets see how to persist them to the database.

I am using FlashBuilder Beta2 with FlexUnit4 RC1. I will go over the integration of FlashBuilder and FlexUnit in another blog posting. For this one I wanted to remain focused on using FlexORM.

Below is a unit test function to test Project CRUD ( create read update delete )

    [Test]
public function testCRUDNoHier():void {
trace("----------------testCRUDNoHier----------------");
var em:EntityManager = EntityManager.instance;

var p:Project = new Project();
p.name="P1";
p.desc = "proj from unit test";
p.color=0xFF001E;
em.save(p);

Assert.assertEquals("P1",p.name);

var p2:Project = new Project();
p2.name="P2";
p2.desc = "proj2 from unit test";
p2.color=0xAAAAAA;
em.save(p2);

Assert.assertEquals("P2",p2.name);
Assert.assertEquals("P1",p.name);

var p1:Project = em.loadItem(Project, p.id) as Project;
Assert.assertEquals("P1",p1.name);

var ps:ArrayCollection = em.findAll(Project);
Assert.assertEquals(2,ps.length);

p1.desc = "New P1 desc";
em.save(p1);

var p3:Project = em.loadItem(Project,p1.id) as Project;
Assert.assertEquals("New P1 desc", p3.desc);

em.remove(p2);
ps = em.findAll(Project);
Assert.assertEquals(1,ps.length);


}



We start the test method by getting a reference to the EntityManager via EntityManager.instance. The first thing we do in the test case, is to create a Project and the save it to the DB via the em.save(p).

That is all you have to do to save an entity, you do not need to write any sql yet.

To load an item for which you have the id, you use the loadItem method passing in the Project class and the id value.

To find all records for a particular class, you use the findAll method.

FlexORM even has a Criteria API. While it is not on the same level as the Hibernate Criteria, it is still very useful.

Below is an example method to find TimeEntries on a particular day:

    public static function findAllOn(fullYear:Number,month:Number=-1,dayOfMonth:Number=-1):ArrayCollection {
var teCriteria:Criteria = entityManager.createCriteria(TimeEntry);
teCriteria.addEqualsCondition("fullYear", fullYear);
if( month > -1 ) teCriteria.addEqualsCondition("month",month);
if( dayOfMonth > -1 ) teCriteria.addEqualsCondition("dayOfMonth",dayOfMonth);
teCriteria.addSort("startHour");

return entityManager.fetchCriteria(teCriteria);
}



You use the EntityManager to create the criteria for an annotated class. In this example I am using simple conditions to see that the TimeEntry has values that equal those passed in, and then sort the returned values based on the starting hour.

Again - so far I have created no sql myself. I am sure I will have a case where I need to hand craft some sql, and this will be fairly easy even with the EntityManager.

The idea behind the ORM tools, IMHO, is not to take over all of the persistence work but instead take over the mundane and tedious persistence work so we can concentrate on the more difficult tasks. So far FlexORM has done that.

I hope this quick example was useful in getting you interested in looking at FlexORM.

Next time I will discuss how I used FlashBuilder Beta2 and FlexUnit to test the application.