- Logikal Blog - http://logikalblog.com -
Java Rots Your Brain
Posted By roland On November 11, 2008 @ 6:21 pm In Information Technology | No Comments
For the past few months now, I have been doing more Java than normal. This week, I started working with Qt and re-writing a system I use to track my expenses for tax purposes. The prior system was in Lotus Approach using DBF files and I wanted something which would work with 64-bit Ubuntu. I now only boot Windows when I have a strong desire to play either Lords of the Realm or Starcraft. It has been a long time since a client has demanded I run Windows and use a software VPN. I assume eventually there will be SISCO VPN software for 64-bit (or at least 32-bit) Ubuntu since that OS seems to be making quite a few inroads on corporate desktops.
Qt shouldn’t be difficult for me. After all, I used Zinc for a good number of years writing stuff on OS/2 which cross compiled for DOS, Windows, and Mac. I even published a couple books on the product. Perhaps it is just because I’m getting older. Perhaps it is because book publishers have forgotten the learning process. For most developers of my era, a new tool or language is learned by actually typing the listings in from the book. The books I’ve found on Qt have devolved into printing just the “relevant snippets” and providing a download link to get the full source. A download link is well and good, so is a CD in the back of the book. It helps those who just want to look at the thing or steal some code for a project. It doesn’t help those who want to learn the product.
Back in my college days, we were told to use one notebook for notes during class, and a second notebook to study from. We were supposed to take quick short notes in class, then copy them cleanly into an official notebook with additional material from our text and other information sources. Some high school classes I had actually graded our notebooks. The reason this was done is the repetition and multiple sensory focus re-enforced the learning process. You don’t learn by simply compiling examples and running them. It is very difficult to talk yourself into re-keying something that is already displayed in a text editor window. Takes a real force of will that most people don’t have.
So, how do we get from problems with Qt learning to Java rotting your brain? Read on.
Database access examples provided by most books are quite pathetic. Since they are focusing on just the needed IO calls/commands, all access information tends to be hard coded in the example. Things like username, database name, password, etc. all hard coded strings. Not the kind of code anyone would want in a production system (but sadly, the kind of code actually existing in a lot of shops.)
The first piece of code you have to develop on your own is a simple logon dialog. Depending upon the toolset you are using this can either be a dialog which prompts for and returns the information, or it can actually take the extra step of validating the connection information and creating the database object. I opted to connect and validate since that allowed me to create a shiny new database if the one I wanted was missing. Silly boy! You want something to function correctly. This is the age of Microsoft, where nothing works and nothing is expected to work!
Once I had that working, I created a second dialog to allow for entry and editing of the expense table while using the category and payee tables as foreign key references. When I added that dialog and tested the simple test of navigating to the screen and exiting it I was greeted with the following error:
QSqlDatabasePrivate::removeDatabase: connection ‘xpns’ is still in use, all queries will cease to work.
Spent around 4 hours searching around on-line and trying various things to eliminate this error. Found a lot of bad advice on how to mask the error and some people saying it was only a warning they could ignore. Well, it’s really neither. You’ve created a memory leak and at least this is letting you know about it.
Part of the problem is that QSqlDatabase objects are global to your process. No matter where they get created in your program, once created they are accessible by any function/method. I have mixed emotions about that, but it is what it is. The other part of the problem was my having worked with Java for a while then reading in the Qt 4 book about how all things derived from the QObject would be cleaned up by the Qt application when it closed. It’s true as far as it goes, but it doesn’t go far enough.
Granted, I should have created a container QWidget and added the login dialog to that, but, I also shouldn’t have been forced into that design since you can create dialogs wherever you want and they will get cleaned up…mostly. The “order” of cleanup leads to problems though. The other issue forcing my decision was the fact the form needed to know which tax year it was dealing with to be created appropriately.
I know, many of you Unix developers would simply put the second form in its own thread where it could run yet another instance of an application and all would be right with the world. I have a personal issue with launching a new thread from a parent thread and allowing the parent thread to die immediately after launching the child. This is EXACTLY what creates dangling threads on systems.
The problem doesn’t have anything to do with the QSqlDatabase class itself, other than it is reference counting since it is global. The problem has to do with the default parameter for a QDialog, most notably
XpnsLogonDialog( QWidget *parent = 0);
Ordinarily, this isn’t an issue. Creating an un-anchored dialog which connects to a database causes the problem. Were it anchored to a QWidget, the QWidget would die and take all its children before the other global things would be reclaimed.
Where I got into trouble was when the dialog needed parameters for its constructor other than a parent value. Those values needed to come from the logon dialog itself. Adding insult to injury was the fact I was looking at code “snippets” which were relying on having a parent QWidget to control life and death of the object holding the database reference. Life is really good some times.
else {
connectionStatus = true;
xpnsForm = new XpnsForm( -1, _tax_year);
xpnsForm->show();
}
The above snippet, executed when the logon information provided parameters which resulted in a valid database connection is where the wheels started to come off the cart. “new” put the allocation on the heap instead of the stack. Due to the way QObject gets cleaned up, the reference counter was still set. I tried everything, closing the database, calling removeDatabase, every suggestion, and still the reference counter caused the warning message. I was thinking this cleanup happened the same way Java garbage collection does. The cleanup would make N passes through the list collecting everything flagged for garbage collection and flagging new list entries as it freed things up. It doesn’t.
When all else fails, you use brute force.
XpnsLogonDialog *dialog = new XpnsLogonDialog();
dialog->show();
i_x = app.exec();
delete dialog;
db.close();
In the XpnsLogonDialog I was forced to create a destructor as well.
XpnsLogonDialog::~XpnsLogonDialog()
{
nuke_subforms();
}
void XpnsLogonDialog::nuke_subforms()
{
if ( xpnsForm != 0) {
delete xpnsForm;
xpnsForm = 0;
}
}
While it is true I didn’t need to break it out into its own function, doing that allowed me to test one other thing.
XpnsLogonDialog *dialog = new XpnsLogonDialog();
dialog->show();
i_x = app.exec();
//delete dialog;
dialog->nuke_subforms();
db.close();
Both methods free up the resources and clear the reference counter.
Spend much time working with Java, and you will stumble when coming back to C++. The same is also true if you spend a lot of time with C++ and try to jump back to Java. Admittedly, this problem surfaced because of someone trying to be nice and rolling their own garbage collection into an application framework. I suspect it solves more problems than it causes. I remember a lot of people having memory issues with their Zinc applications because there wasn’t newbie friendly documentation, just some example programs people copied from, whether they applied to their project or not.
One other major issue I have with the published examples have to do with the following code. This code is mine and I have fixed the issue.
//
// Actual database setup begins here. Keep in mind we have already
// established a connection to the database at this point so fieldIndex()
// should work correctly.
//
tableModel = new QSqlRelationalTableModel(this, QSqlDatabase::database(”xpns”));
tableModel->setTable(”expenses”);
tableModel->setRelation(tableModel->fieldIndex(”category”),
QSqlRelation(”categories”, “category”, “category”));
tableModel->setRelation(tableModel->fieldIndex(”payee”),
QSqlRelation(”payees”, “payee”, “payee”));
tableModel->setSort(tableModel->fieldIndex(”tran_dt”), Qt::AscendingOrder);
tableModel->select();
In the published examples, the create an enum for the field number and pass in compiled values to these and other database calls. That is an extremely bad practice, especially since the library already has the tools you need. Above, you will see I called fieldIndex() to obtain the column number from the currently connected database table.
Can anyone tell me why using a compiled enum is a bad practice?
That’s right! The database layout could change over time and you don’t want to be married to it. A five column example table has little chance of that, but what about a production table? What if you are only using five fields out of a production table that has 20 and the DBAs have to drop a column occurring before the columns you use? Sometimes you don’t even know about a change. The DBAs aren’t usually send out change request email that most developers ignore. Sometimes for performance reasons, they need to change the structure of a database. They do an unload and a reload, only the new table they reload into has the fields in a different order. You find out at 3 a.m. on Monday morning when your application won’t start and production is held up.
Wow! A rant AND useful information.
Article printed from Logikal Blog: http://logikalblog.com
URL to article: http://logikalblog.com/2008/11/11/java-rots-your-brain/
Click here to print.