ValidatorCFC Update

Just a quick note, thanks again to Aaron Greenlee I have some small modifications to ValidatorCFC.

The modifications allow you to return a struct where the key is the name of the field in your object, and the value is true/false indicating if it passed validation.

This is useful if you prefer more control when showing informative messages to the user.

The zip file is attached to this entry as well as at RIAForge.

Adobe releases hotfix for FCKEditor issue

Adobe has released a hotfix for the FCKeditor issue. You can find it here.

Take notice that it says to first stop the CF service and then access the CF administrator. This is impossible. Simple open the administrator first and apply the hot fix on the system info screen.

Also,

Mac (linux too??) users should know that if you unzip the cfide.zip file to the cfide folder it will replace the folder not merge the files like on windows. You will be left without a working CFIDE folder. You should manually merge the files.

Quick Note

Just a quick note to say that the zip file on RiaForge for ValidatorCFC (http://validatorcfc.riaforge.org/) was incorrect. Thanks to Bill D (http://brainbox.tv) for the head's up.

I also attached it to this post. Use the download link below.

Follow me on Twitter

http://twitter.com/nickel4242

Update to ValidatorCFC

I released a new version of ValidatorCFC. Aaron Greenlee (http://www.aarongreenlee.com) sent me some changes. With his changes, you can now specify which properties you want validated. So even if in your object you say that last name and first name are required, if you pass in only the last name property, only that field will be validated. There is more info within the CFC itself, and in the test files in the zip. You can download the zip file using the download link on this entry, or from RIAForge.

Transfer Error when using ntext or text and a ManyToMany Relationship

When you set Transfer to use a table that has an ntext or text column on SQL Server 2000 (and perhaps later versions) and also a many to many relationship you may receive the following error when you attempt to retreive the collection for the many to many:

view plain print about
1[Macromedia][SQLServer JDBC Driver][SQLServer]Only
2text pointers are
3allowed in work tables, never text, ntext, or image
4columns. The query
5processor produced a query plan that required a
6text, ntext, or image
7column in a work table.

For example I have the following tables set up

view plain print about
1Table Name:
2Table1
3id        char(35)    primary key
4title        
5varchar(255)    
6description    ntext
7
8Table Name: Table2
9id        
10char(35)    primary key
11description    
12varchar(255)
13
14Table Name: Table1_Table2
15table1_id    char(35)    
16composite key
17table2_id    char(35)    composite
18key

And the following transfer configuration:

view plain print about
1<transfer>
2    <objectDefinitions>
3        
4<object name="Table1" table="table1">
5            <id
6name="id" type="UUID" generate="true" />

7            
8<property name="title" type="string" nullable="false" />
9            
10<property name="description" type="string" nullable="true" />
11            
12<manytomany name="Relation" table="Table1_Table2" lazy="true">
13                
14<link to="Table1" column="table1_id" />
15                
16<link to="Table2" column="table2_id" />
17                
18<collection type="array">
19                    <order
20property="description" order="asc" />

21                
22</collection>
23            </manytomany>    
24        
25</object>
26        <object name="Table2" table="Table2">
27            
28<id name="id" type="UUID" generate="true" />
29            
30<property name="description" type="string" nullable="false" />
31        
32</object>
33    </objectDefinitions>
34</transfer>

So when you call getRelationArray() on a Table1 object. You will receive the error. To correct this I took the following steps:

Create a for table 1 with the ntext column casted to varchar:

select id,title,cast(description as varchar(8000)) as description from table1

Save this as vwTable1.

Change your transfer config to point to the view instead of table one so

view plain print about
1<object
2name="Table1" table="table1">

becomes

view plain print about
1<object
2name="Table1" table="vwTable1">

Now, you wont be able to run it yet because the view is not updatable since you used the casted field. To overcome this we are going to use an INSTEAD OF trigger.

You need to create 2 triggers. One will be called whenever UPDATE is called on the vwTable1 and the other will be called when INSERT INTO is called on vwTable1

You can create the triggers as follows, obviously change it to suit your table's needs:

view plain print about
1CREATE trigger io_trigger_insert_vwTable1 on
2vwTable1
3instead of insert
4as
5begin
6    set nocount
7on
8    if (not exists (select t.[id] from table1 t, inserted i where
9t.[id] = i.[id]))
10        insert into table1
11        
12select
13            [id],
14            
15[title],
16            [description]
17        from
18
19            inserted
20end

view plain print about
1create
2trigger io_trigger_update_vwAd on vwAd
3instead of update
4as
5
6begin
7set nocount on
8if (exists (select t.[id] from table1 t,
9inserted i where t.[id] = i.[id]))
10    update
11        table1
12
13    set
14        title = i.title,
15        
16[description] = i.[description]
17    from
18        table1
19t, inserted i
20    where
21        t.[id] =
22i.[id]
23end

Once you have created these triggers you should be able to use Transfer as you normally would. Using the casted column in the view will allow you to call the getRelationArray(). The SQL that Transfer creates will no longer be invalid.

You may run into a couple of other items though. First, your ntext field is now limited to 8000 characters since you are casting it to varchar. You may be able to increase this, but I haven't tried. Your mileage may vary, but be sure before the value is passed to transfer that it is validated as less than 8000 characters (perhaps in your decorator). Next, if you have any non-null, defaulted columns in your base table (a created date perhaps) you HAVE to provide a value when you call insert into vwTable1. Since Transfer is creating this SQL you have to ensure that Transfer includes that column. This means that you cannot use the ignore-insert="true" option in the transfer configuration. SQL Server will still respect your default value, as long as you provide that column in the sql statement pointed at the view.

You can find more information about INSTEAD OF INSERT and INSTEAD OF UPDATE triggers on the MSDN site.

I have only used this in a simple application with two base tables and one relationship table, so if your setup is more complex it may not work out for you.

Webservices Change in CF 8.0.1

It seems with the change to 8.0.1 there is a difference in the way CF generates the stubs for a CFC called as a web service. I had a CFC called webservices.cfc and on 8.0 it was working fine calling the methods, but with the 8.0.1 it started throwing a "duplicate file name" error. This seems to be because CF creates its own stub file Webservices.java and also a [cfcname].java file. CF couldn't create both so it threw an error. The solution was to rename my CFC. The stubs can be found in C:\ColdFusion8\stubs\ on windows, and if I remember correctly /Applications/ColdFusion8/stubs/ on Mac OS X.

Issue w/ Eclipse Resource Bundle Editor Plugin and Leopard

There seems to be an issue with the Resource Bundle Editor Plugin for Eclipse. (http://sourceforge.net/projects/eclipse-rbe/)

I installed version 0.7.7 into a fresh install of the Eclipse 3.3 SDK and was unable to open a resource file. I would get the following error:

view plain print about
1Can't start the AWT because Java was started on
2the first thread. Make sure StartOnFirstThread is not specified in your
3application's Info.plist or on the command line

To fix it I edited the eclipse.ini file (/Applications/eclipse/Eclipse.app/Contents/MacOS/eclipse.ini by default) to add the following line:

view plain print about
1-Djava.awt.headless=true

Once I did that it was fine and I was able to edit the resource bundles.

Hope this helps someone searching for the issue!

ColdFusion ORM - Developing the Business Objects First

Since I have transitioned from a world of spaghetti code through the land of the uber-cfc, and finally onto some OO concepts, I have tried to figure out what the best way to create the business objects and persist them to the database. For nearly ever application I have developed, I have created the database first, since, in my mind, understanding the data is the first step to understanding the way the application will function.

However, in the OO world, the primary step is to design the business objects. In my latest project I decided to take one step deeper into the OO world and develop all my business objects forgetting all about how the database would be laid out. I had no problems with this, authoring complex objects containing arrays or instances of other objects without issue.

Then I needed to figure out how to persist them. In the past, each of my business objects directly mapped to a table in my database. This made it easy to build a code generator, read in the DB metadata and, in the words of John Madden, "Boom", I had my beans, DAOs, and Gateways.

Now, I see what Sean Corfield refers to as the "5:1 Syndrome". I have a dumb bean object, a DAO, a Gateway, a Service and, if using Model-Glue, a Controller for nearly every object, and in turn every table in the database. This always seemed like code bloat to me, so I was very happy to see other, like Sean, refer to it as such. There must be a better way!

Trying to avoid this I decided, OK I'll use Reactor. Active Record, generated code, sounds good. Then I realized that I have to create the database first. Back to square one.

Should business objects reflect the organization of the database? They can, but if it doesn't fit the way the business object is used in the application, then no, it shouldn't. Also, from the other way, the best way to organized the business objects is probably not the best way to organize the database. The database should be in 3NF, avoid duplication whereever possible, etc, etc.

Not wanting to trash all the BOs I created already, I decided I would write all the persistance stuff myself. So now that I have some beans, I created a service for each and wired them up in ColdSpring. I put all the methods that would normally go in my DAO (save, new, and CRUD) in my service as well as some getAll() calls.

As a side note, I don't return query objects since, even though they are faster than creating an array of objects, I cant call a complex getter, such as a calculated value easily, and this allows me to use the same calls to get the data for one instance of an object as I do for a collection.

Now I have a BO and a service that is completely separate from the DB. Persisting complex objects is a bit more complicated than using the 1:1 BO to DB mapping, but it works. The only issue is, I don't want to have to maintain all this manually. I wish we had a mapping tool to handle persistance from BO to DB without requiring that the DB reflect the BO or vice versa.

Java has Hibernate to handle this issue. With CF8 we can now use Hibernate with a Java model, but what if we want to model using CFCs? cfHibernate attempted to do this and seems to have run into some issues. Can we use Hibernate to map CF objects to the DB? I am going to attempt to find an answer for this in the next few weeks for my current project. Hopefully I can find a solution.

ColdFusion 8 Released!

Just a quick note that CF8 has been released by Adobe. If you haven't seen the new features, download a copy and see for yourself.

Previous Entries / More Entries

Fork me on GitHub