June 10, 2006

It gets worse….

Writing about web page /colinyates/entry/hsbc_love_them/

Writing about an entry you don't have permission to view

So I have just received the first (hopefully of many) cheque for commission for a book I co-wrote (Expert Spring MVC + Web Flow) from the American publishers Apress.

OK, off to HSBC I trotted (across the electronical wire via the telephone) and enquired as to how much this would translate into when cashed into UK pounds…..and they didn’t know. Eventually their help desk managed to find someone who kinda knew and they informed me it would cost a whopping £28 and six weeks to clear!

They were having a laugh….£28 is ridiculous, as is 6 weeks!

Anyway, off I trotted (via the telephone) to Cahoot to ask them and was told it would still take 6 weeks, but they would only charge £10.

Ok, so maybe 6 weeks is the time it takes the English computer to understand the American computer’s bizarre use of the English language (:)), but why does it cost HSBC almost 3 times as much as it costs Cahoot?

Oh that’s right, it doesn’t. They are rip off merchants.

They suck.


June 08, 2006

Interesting oracle funniness

So I have a table content which has 30500 rows, it has a one to many relationship with content_fetcher, which has 50700 rows.

I have removed a number of rows from content, and I now want to remove the orphaned rows from content_fetcher (cascade delete; ha, who needs it :)).

So, trivially easy SQL to find out the number of orphaned content_fetchers…

select count(id) from content_fetcher where content_id not in (select id from content)

times out :( Why?

Bizarrely, calculating the number of content_fetchers that are not orphaned works:

select count(id) from content_fetcher where content_id in (select id from content)

(30622 rows BTW)

so, we know how many valid content_fetchers there are… well, counting the number of content_fetchers that are not in that set, surely that will hit the same "feature"?:

select count(id) from content_fetcher where id not in (
  select id from content_fetcher where content_id in (select id from content)
);

Nope; it works. 20103 rows.

Go figure….

(deletion took 22 seconds BTW)


May 15, 2006

JUnit 4.0; whats all the fuss (aka jumping on the annotations bandwagon)

Writing about web page http://www.instrumentalservices.com/index.php?option=com_content&task=view&id=45&Itemid=52

I love junit. I think it is probably one of the best pieces of software around. It easily allows developers to follow one of the best methodologies around; test driven development, or at least development with tests :)

I do not think JUnit is perfect; there are bugs (google for more details…), and I was really hoping JUnit 4.0 would solve some of them.

Unfortunately, JUnit 4.0 has “fixed” all the wrong things :( Of course, this is only my opinion, and I am very grateful for all their hard work, but I am dissappointed.

Basically; the main difference between JUnit 3.0 and 4.0 is the infrastructure used to setup and define tests…JUnit 3.0 uses abstract classes and naming conventions; JUnit 4.0 uses annotations. Arguably having to not extend a base class is always good (less pervasive), but I think this is less so with unit tests (see later)

In a nutshell Rather than having

public class MyTestClass extends TestCase {
  private Object myObject;

  public void setUp() {
    myObject = new myObject();
  }

  public void testFirstAssumption() {
    assertTrue("hello world", myObject.getHelloWorld());
  }

  public void tearDown() {
    myObject = null;  // completely unneccessar, but for illustration
  }
}

now you would do:

public class MyTestClass {
  private Object myObject;

  @Before public void setUp() {
    myObject = new myObject();
  }

  @Yest public void firstAssumption() {
    assertTrue("hello world", myObject.getHelloWorld());
  }

  @After public void tearDown() {
    myObject = null;  // completely unneccessary, but for illustration
  }
}

whats the difference; I am using annotations instead of naming conventions….what has it bought me in real terms... absolutely nothing. I still have to explicitly mark a test method, the setupMethod, tearDown method etc., I just use annotations instead of naming conventions.

I am of course being entirely unfair….you can do more things with the annotations like having more than one setup method, commenting out test methods (@Ignore), indicating your test expects an exception (@Test(expected=yourException) but so what?

The only thing of value is the @test(timeout=X) method which natively indicates that the test must execute within X milliseconds. Very useful; but there are already extensions to do this.

Extending framework classes are bad

It is true that frameworks which require you to extend a base class are so 1980s :) but with unit tests, I actually don’t think it is such a bad thing…. the problem is that you cannot extend your own class because Java doesn’t support multiple inheritence (for better or worse…). However, the point is that unit tests are supposed to be small atomic tests which test a single thing. They are not supposed to be complicated, hierarchical object graphs which build layer upon layer of logic. If I cannot understand what is happening from looking at a single unit test…I consider that unit test less than optimal. I know a lot of my coworkers dissagree with me on this, but I would rather see code duplication in a test method rather than a parameterised method … simply for understandability. Let me say it again, I think any unit test method should be self contained for readability. Yeah; go on, flame away :) The worst thing you can have is unit tests breaking because you have modified another unit test ;) Unit tests breaking because of changes to domain code is good, breaking because of changes to unit tests are bad. Very bad…

So, after that lengthy paragraph, the fact that JUnit 3.0 imposes a relatively inflexible infrastructure in which to write your unit tests is not so much a good thing, but it does prevent you over complicating your unit tests.

Summary

So, is JUnit 4.0 is a dissappointment, no (despite the previous paragraph). Do I think JUnit 4.0 is a massive improvement; no. I do see how annotations are useful (timing, repeating etc.), but I feel somewhat underwhelmed :)


May 10, 2006

SpringOne travel arrangments; Eurostar rocks :)

Writing about web page http://www.springone.com/display/SpringOne06/Home

So I am off to SpringOne in June and was googling for how much it would cost. Well the good news is that there are lots of hits on flying to Antwerp, some from £115 return :)

Only they aren't really that good. They either require leaving mid day on the Friday and take 7 hours because of 2 change overs(!!) or suddenly become £400 if you fly back on the Saturday.

Travelling from London City Airport is slightly better, although still quite expensive and painful to get to (from Leicester). Manchester is equally painful and East Midlands is a no–go.

I was basically faced with spending around £150 to attend at best a day and a half conference which is already costing me ~500 euros :(

After typing up a cancellation email, I noticed a link to train times to Antwerp… It turns out that Eurostar travels from London Waterloo to any station in Belgium from as littler as £29 each way! Bargain. They also arrive/leave at useful times as well. Eurostar drops you at the main central station in Bruxelles Midi, but it is only a one hour train to Antwerp, and I understand they are fairly regular. So, all I have to do is travel to London from Leicester by train. Initially, virgin are trying to charge me ~£50 each way, but I noticed a "value" link which when pressed shows you a list of really cheap tickets…those sneaky guys ;)

So, my travel arrangments are:

Leicester –> London Waterloo (£8!!!)

London Waterloo –> Bruxelles Midi (+ free ticket to any Belgium station) (£29.50)

way home:

Bruxelles Midi –> London Waterloo (£39.50)

London Waterloo –> Leicester (£8!!!)

A grand total of £85. That is cheaper than the cheapest one way flight to Antwerp that I could find (excluding air tax, travelling to London/BHam/Manchester etc.) and the journey door to door will be quicker and less stressful.

Long live Eurostar :)


May 09, 2006

A better explanation than mine :)

Follow-up to Java Pet Peeves #3:Lack of use of the final keyword from Colin's blog

This guys goes into the ins and outs of the final keyword. Not sure he is saying anything that I didn't, but he does say it better and as my wife keeps stating "it's not what you say, it is the way that you say it" :) :)

link


May 05, 2006

Spring Web Flow RC1.0 – mapping comments

Writing about web page http://www.springframework.org/documentation

So I have downloaded SWF RC1.0 and doing the upgrade. This post if really just for me to keep track of things :)

Input mapping when calling a subflow

The source must be an expression, i.e. ${model.someProperty} whereas the target must be text:
<mapping source="${model.uploadedFiles}" target="uploadedFiles"/>

Output mapping when calling a subflow

The source must be text i.e. someProperty whereas the target must also be text, but qualify the scope:
<mapping source="emptyFiles" target="flowScope.emptyFiles"/>

Input mapping for the subflow itself

The subflow must now explicitly slurp the inputs.
The source must be text i.e. someProperty whereas the target must also be text, but qualify the scope:
<mapping source="uploadedFiles" target="flowScope.uploadedFiles"/>

Output mapping for the subflow itself (i.e. end state)

The subflow must now explicitly export the results that the calling flow can slurp.
The source must be an expression i.e. ${flowScope.someProperty} whereas the target must be text:
<mapping source="${model.emptyFiles}" target="emptyFiles"/>

Essentially, when a subflow starts, the calling flow exports bits of its context into a generic map, so source is an expression, target is the key in the map. The subflow (when it starts) can slurp from that generic map using the known key, so source is the key, target is where in the subflow context it must be placed (as plain text, not expression).

When the subflow ends, it exports bits of its context using the same rationale; source is an expression, target is the key in the generic map. The calling flow can then slurp from that generic map; source is key, target is plain text description of where it is placed.

example:

  <flow ...>
    <subflow-state ...>
   <attribute-mapper>
      <input-mapper>
       <mapping source="${model.uploadedFiles}" target="uploadedFiles"/>
      </input-mapper>
      <output-mapper>
        <mapping source="emptyFiles" target="flowScope.emptyFiles"/>
        <mapping source="invalidFileNames" target="flowScope.invalidFileNames"/>
        <mapping source="invalidFileTypes" target="flowScope.invalidFileTypes"/>
        <mapping source="duplicateFiles" target="flowScope.duplicateFiles"/>
        <mapping source="createdFiles" target="flowScope.createdFiles"/>
      </output-mapper>
    </attribute-mapper>
    </subflow-state>
  </flow>

  



...









Note: nothing in here is the definitive way; but it does work :)


April 27, 2006

Some more hibernate thingies

Writing about web page http://hibernate.org

Some more hibernate observations, although I haven't got time to prove via unit tests:

– if you specify an interface="" then the laziness is ignored; it is always lazy (not documented AFAICS)

– if you have final methods/class then regardless of access (field|property) you will get null values. This is because CGLIB cannot subclass/override final classes/methods (well known), but why doesn't hibernate throw an exception?

– if you specify non-lazy for a class, then the class is fully formed, even if it is using CGLIB with final classes/methods irrespective of access (property|field). I suspect in this case CGLIB isn't involved and hibernate is simply instantiating your implementation class and updating the fields via reflection.

– if you specify an outer join in the mapping, laziness will be ignored; it won't be lazy. This of course is intuitive; join makes no sense if it is not lazy.

When I have more time I will unit test all these combinations, but this is a reminder to me do so.

Will I ever understand hibernate completely :)


April 25, 2006

Hibernates bizarre interpretation of inverse ;)

Writing about web page hibernate.org

So my colleague asked me about the exact semantics of hibernate's "inverse" keyword which is surely the worst choice of word for a simple concept.

Essentially "inverse" indicates which end of a relationship should be ignored, so when persisting a parent who has a collection of children, should you ask the parent for its list of children, or ask the children who the parents are?

Why does this matter? Well if you are stupid then you might do something like:


  Parent parentA = new Parent();
  Parent parentB = new Parent();
  Child child = new Child();
  parentA.getChildren().add(child);
  child.setParent(parentB);

how should hibernate persist this situation? For unidirectional one–to–many it is trivial; only one end of the relationship is modelled (there is only parent.addChild(), not child.getParent()), but when it is bidirectional (parent.getChild and child.getChildren) you need to indicate whether the one–to–many is inverse or not.

What does it mean to be inverse? It informs hibernate to ignore that end of the relationship. If the one–to–many was marked as inverse, hibernate would create a child–>parentB relationship (child.getParent). If the one–to–many was marked as non–inverse then a child–>parentA relationship would be created.

Or at least that is what you would think :) First moan is why use the word "inverse"? By setting it to true you are effectively telling hibernate to ignore it. Setting something to true to exclude something is reverse logic and is incredibly unintuitive. Maybe "owner" would be a better term?

The second moan is that hibernate isn't consistent. If the parent–>child is not inverse (i.e. parent.getChildren will be used to define the relationship) then you would expect calling child.setParent to have no effect. This is not the case. Calling child.setParent does create the relationship. If you end up with the idiotic case I proposed earlier, than hibernate will give precendece to the parent.getChildren (as it should), but to be consistent child.setParent(parentA) should not create the relationship.

It is such a simple concept, but so many people seem to struggle with it. I really don't think hibernates terminology is very helpful.

Simply put; if you set inverse=true on a one–to–many then child.getParent will be called, if inverse=false then parent.getChildren will be called.

Of course, good developers will not run into this problem because they will all ensure that parents have appropriate accessors (parent.addChild, parent.removeChild etc.) :)

The invertedness (?) of the relationship does have quite an impact on performance and schema design; if the one–to–many is not inverse (default) then everytime you create a child, hibernate will execute 2 statements, one to create the child, one to update the child with the foreign key of the parent. Foreign key constraints/not null constraints? hah, who needs those anyway :) Hibernate will also instantiate the collection everytime you add a child….not sure why. Setting inverse to true will fix both of these annoyances/downright ridiculous design decisions.

One last note; cascade and inverse are completely orthogonal. Cascade simply tells hibernate how to reach objects. If I set inverse=true on a one–to–many then hibernate will not use parent.getChildren to define the relationship. If I set cascade to save, or all then hibernate will call parent.getChildren to see if there are any new children. Setting inverse=true and calling child.setParent will have absolutely no effect because hibernate won't know about your new child :)

So summary:

– always provide parent.addChild which updates both ends of the relationship

– always set inverse to true on bidirectional one–to–many

– make sure you understand the difference between cascades and inverse ;)

P.S. Version 3.1.2 was used.

Code used for inverse relationship


<?xml version="1.0"?>
<!DOCTYPE hibernate–mapping PUBLIC
    "–//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate–mapping–3.0.dtd">

<hibernate–mapping>
  <class name="uk.ac.warwick.Child" table="child">
    <id name="id"
        column="id"
        type="java.lang.String"
        unsaved–value="null">
        <generator class="org.hibernate.id.UUIDHexGenerator"/>
    </id>
    <many–to–one name="parent"
                 class="uk.ac.warwick.Parent"
                 column="parent_id"/>
  </class>
</hibernate–mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate–mapping PUBLIC
    "–//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate–mapping–3.0.dtd">

<hibernate–mapping>
  <class name="uk.ac.warwick.Parent" table="parent">
    <id name="id"
        column="id"
        type="java.lang.String"
        unsaved–value="null">
        <generator class="org.hibernate.id.UUIDHexGenerator"/>
    </id>
    
    <bag name="children" inverse="true" cascade="save–update">
      <key column="parent_id"/>
      <one–to–many class="uk.ac.warwick.Child"/>
    </bag>
  </class>
</hibernate–mapping>

package uk.ac.warwick.inverse;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import uk.ac.warwick.Child;
import uk.ac.warwick.Parent;

public final class TestRelationships extends TestCase {
    public void testChildrenOfParentAreIgnored() {
        Configuration config = new Configuration();
        config.addResource("uk/ac/warwick/inverse/Child.hbm.xml");
        config.addResource("uk/ac/warwick/inverse/Parent.hbm.xml");
        
        config.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        config.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
        config.setProperty("hibernate.hbm2ddl.auto", "create–drop");
        config.setProperty("hibernate.show_sql", "true");
        config.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
        config.setProperty("hibernate.connection.username", "sa");
        
        SessionFactory factory = config.buildSessionFactory();
        Session session = factory.openSession();
        
        Parent parentA = new Parent();
        session.save(parentA);
        session.flush();
        
        /**
         * Because parent–>child is inverse, adding the child to the parent
         * will not associate it with the parent.
         * 
         * Cascade will save it though.
         */
        Child childA = new Child();
        parentA.getChildren().add(childA);
        session.save(parentA);
        session.flush();

        session.evict(childA);
        assertEquals("cascade works so child should be saved",
                     1,
                     session.createCriteria(Child.class).list().size());

        childA = (Child) session.load(Child.class, childA.getId());
        assertNull("childAs parent should not be set!",
                   childA.getParent());

        session.evict(parentA);
        parentA = (Parent) session.load(Parent.class, parentA.getId());
        assertTrue("but it shouldnt update the children of parentA",
                   parentA.getChildren().isEmpty());
        
        childA.setParent(parentA);
        session.save(childA);
        session.flush();
        
        /**
         * Because child–>parent is not inverse, setting the parent
         * will create the association 
         */
        childA.setParent(parentA);
        session.save(childA);
        session.flush();

        session.evict(childA);
        childA = (Child) session.load(Child.class, childA.getId());
        assertEquals("childAs parent should now be set!",
                   parentA,
                   childA.getParent());

        session.evict(parentA);
        parentA = (Parent) session.load(Parent.class, parentA.getId());
        assertFalse("and parentA should have children",
                   parentA.getChildren().isEmpty());
     }
}

Code used for non inverse relationship


<?xml version="1.0"?>
<!DOCTYPE hibernate–mapping PUBLIC
    "–//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate–mapping–3.0.dtd">

<hibernate–mapping>
  <class name="uk.ac.warwick.Child" table="child">
    <id name="id"
        column="id"
        type="java.lang.String"
        unsaved–value="null">
        <generator class="org.hibernate.id.UUIDHexGenerator"/>
    </id>
    <many–to–one name="parent"
                 class="uk.ac.warwick.Parent"
                 column="parent_id"/>
  </class>
</hibernate–mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate–mapping PUBLIC
    "–//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate–mapping–3.0.dtd">

<hibernate–mapping>
  <class name="uk.ac.warwick.Parent" table="parent">
    <id name="id"
        column="id"
        type="java.lang.String"
        unsaved–value="null">
        <generator class="org.hibernate.id.UUIDHexGenerator"/>
    </id>
    
    <bag name="children" inverse="false" cascade="save–update">
      <key column="parent_id"/>
      <one–to–many class="uk.ac.warwick.Child"/>
    </bag>
  </class>
</hibernate–mapping>

package uk.ac.warwick.non_inverse;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import uk.ac.warwick.Child;
import uk.ac.warwick.Parent;

public final class TestRelationships extends TestCase {
    public void testParentOfChildrenAreIgnored() {
        Configuration config = new Configuration();
        config.addResource("uk/ac/warwick/non_inverse/Child.hbm.xml");
        config.addResource("uk/ac/warwick/non_inverse/Parent.hbm.xml");
        
        config.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        config.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
        config.setProperty("hibernate.hbm2ddl.auto", "create–drop");
        config.setProperty("hibernate.show_sql", "true");
        config.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
        config.setProperty("hibernate.connection.username", "sa");
        
        SessionFactory factory = config.buildSessionFactory();
        Session session = factory.openSession();
        
        Parent parentA = new Parent();
        session.save(parentA);
        session.flush();

        /**
         * Even though child–>parent is inverse, setting the parent
         * will associate it with the parent.
         */
        Child childA = new Child();
        childA.setParent(parentA);        
        session.save(childA);
        session.flush();

        session.evict(childA);
        assertEquals("cascade works so child should be saved",
                     1,
                     session.createCriteria(Child.class).list().size());

        childA = (Child) session.load(Child.class, childA.getId());
        assertNotNull("childAs parent will be set!",
                     childA.getParent());

        session.evict(parentA);
        parentA = (Parent) session.load(Parent.class, parentA.getId());
        assertFalse("and it will update the children of parentA",
                   parentA.getChildren().isEmpty());
     }
}

April 24, 2006

Exciting new Mustang features (Java SE 6)

Writing about web page http://java.sun.com/javase/6/download.jsp

So I just noticed that version 6 (beta) of the J2SE is out and decided to have a browser through the release notes: link.

A couple of things caught my eye:

link (Compiler API)

I imagine this will drastically improve IDE internal compile/code completion functionality no end. I can also envisage some scenarios where this would be useful from in an application; maybe an alternative to weird and wonderful aspects aspects ;)

link (Scripting support)

This I am sure will cause a stir, and everybody will start writing "frameworks" which allow rules to be written in a scripting language….only to find that actually all they have done is lost the richness and strong typing of Java and haven't really gained anything.

I am not saying Scripting is a bad thing; only that it has it's place…. Thats my opinion anyway :)

link (Common annotations)

Well this can only be a good thing really.

link (Lightweight HTTP server)

If this isn't the most ridiculous waste of time ever …. wheel … re-invent (probably badly) ….

link (Classpath wildcards)

About time. I cannot believe it has taken so long for this to come into play.

It looks very promising. There are also a lot of performance enhancements which could be promising.

Let's wait and see.


December 2019

Mo Tu We Th Fr Sa Su
Nov |  Today  |
                  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               

Search this blog

Tags

Galleries

Most recent comments

  • Interesting… While I'm not completely convinced in such microbenchmarks, I'm pretty sure that 1ms … by Alexander Snaps on this entry
  • Hello. I bought the book yesterday. I was trying to find the source code for chapter 11 and chapter … by Suleman on this entry
  • http://woosight.net/account/login?username=demo by live mashup demo on this entry
  • Thanks mate ….. This blog was really helpful. by Maaz Hurzuk on this entry
  • Ty. Not directly helpful for my problem, but pointed me in the right direction. You will also get th… by Mike E. on this entry

Blog archive

Loading…
RSS2.0 Atom
Not signed in
Sign in

Powered by BlogBuilder
© MMXIX