views:

1469

answers:

9

I'm making a mini ORM for a Java program I'm writing... there is a class for each table in my db, all inheriting from ModelBase.

ModelBase is abstract & provides a bunch of static methods for finding & binding objects from the db, for example:

public static ArrayList findAll(Class cast_to_class) {
  //build the sql query & execute it 
}

So you can do things like ModelBase.findAll(Albums.class) to get a list of all persisted albums. My problem is that in this static context, I need to get the appropriate sql string from the concrete class Album. I can't have a static method like

public class Album extends ModelBase {
  public static String getSelectSQL() { return "select * from albums.....";}
}

because there is no polymorphism for static methods in Java. But I don't want to make getSelectSQL() an instance method in Album because then I need to create an instance of it just to get a string that is really static in behaviour.

At the moment, findAll() uses reflection to get the appropriate sql for the class in question:

select_sql = (String)cast_to_class.getDeclaredMethod("getSelectSql", new Class[]{} ).invoke(null, null);

But that's pretty gross.

So any ideas? It's a general problem I'm having time and time again - the inability to specify abstract static methods in classes or interfaces. I know why static method polymorphism doesn't and can't work, but that doesn't stop me from wanting to use it time again!

Is there any pattern/construct that allows me to ensure that concrete subclasses X and Y implement a class method(or failing that, a class constant!)?

A: 

If you are passing a Class to findAll, why can't you pass a class to getSelectSQL in ModelBase?

asterite
+1  A: 

Why not using annotations? They fitpretty well what you're doing: to add meta-information (here an SQL query) to a class.

gizmo
A: 

asterite: do you mean that getSelectSQL exists only in ModelBase, and it uses the passed in class to make a tablename or something like that? I cant do that, because some of the Models have wildy differeing select constructs, so I cant use a universal "select * from " + classToTableName();. And any attempt to get information from the Models about their select construct runs into the same problem from the original question - you need an instance of the Model or some fancy reflection.

gizmo: I will definatly have a look into annotations. Although I cant help but wonder what people did with these problems before there was reflection?

dalyons
Then you should take a look at ORM in languages that does not support reflection, such as C++. Because as far as I know, neraly all the Java ORM (if not all) uses reflections at some point.
gizmo
Please make those comments as... comments ;) An answer if for answer only. That way, gizmo and asterite will *edit* their answers to complete them, or will leave further comments to continue the discussion initiated by your questions
VonC
A: 

You could have your SQL methods as instance methods in a separate class.
Then pass the model object into the constructor of this new class and call its methods for getting SQL.

Darren Greaves
A: 

I agree with Gizmo: you're either looking at annotations or some sort of configuration file. I'd take a look at Hibernate and other ORM frameworks (and maybe even libraries like log4j!) to see how they handle loading of class-level meta-information.

Not everything can or should be done programmatically, I feel this may be one of those cases.

Tim Mooney
A: 

Wow - this is a far better example of something I asked previously in more general terms - how to implement properties or methods that are Static to each implementing class in a way that avoids duplication, provides Static access without needing to instantiate the class concerned and feels 'Right'.

Short answer (Java or .NET): You can't. Longer answer - you can if you don't mind to use a Class level annotation (reflection) or instantiating an object (instance method) but neither are truly 'clean'.

See my previous (related) question here: http://stackoverflow.com/questions/66505/how-to-handle-static-fields-that-vary-by-implementing-class I thought the answers were all really lame and missed the point. Your question is much better worded.

Ewan Makepeace
+1  A: 

As suggested, you could use annotations, or you could move the static methods to factory objects:

public abstract class BaseFactory<E> {
    public abstract String getSelectSQL();
    public List<E> findAll(Class<E> clazz) {
       // Use getSelectSQL();
    }
}

public class AlbumFactory extends BaseFactory<Album> {
    public String getSelectSQL() { return "select * from albums....."; }
}

But it is not a very good smell to have objects without any state.

Bruno De Fraine
+4  A: 

Static is the wrong thing to be using here.

Conceptually static is wrong because it's only for services that don't correspond to an an actual object, physical or conceptual. You have a number of tables, and each should be represented by an actual object in the system, not just be a class. That sounds like it's a bit theoretical but it has actual consequences, as we'll see.

Each table is of a different class, and that's OK. Since you can only ever have one of each table, limit the number of instances of each class to one (use a flag - don't make it a Singleton). Make the program create an instance of the class before it accesses the table.

Now you have a couple of advantages. You can use the full power of inheritance and overriding since your methods are no longer static. You can use the constructor to do any initialisation, including associating SQL with the table (SQL that your methods can use later). This should make all your problems above go away, or at least get much simpler.

It seems like there is extra work in having to create the object, and extra memory, but it's really trivial compared with the advantages. A few bytes of memory for the object won't be noticed, and a handful of constructor calls will take maybe ten minutes to add. Against that is the advantage that code to initialise any tables doesn't need to be run if the table isn't used (the constructor shouldn't be called). You will find it simplifies things a lot.

DJClayworth
+1  A: 

Albeit, I totally agree in the point of "Static is the wrong thing to be using here", I kind of understand what you're trying to address here. Still instance behavior should be the way to work, but if you insist this is what I would do:

Starting from your comment "I need to create an instance of it just to get a string that is really static in behaviour"

It is not completely correct. If you look well, you are not changing the behavior of your base class, just changing the parameter for a method. In other words you're changing the data, not the algorithm.

Inheritance is more useful when a new subclass wants to change the way a method works, if you just need to change the "data" the class uses to work probably an approach like this would do the trick.

class ModelBase {
    // Initialize the queries
    private static Map<String,String> selectMap = new HashMap<String,String>(); static {
        selectMap.put( "Album", "select field_1, field_2 from album");
        selectMap.put( "Artist", "select field_1, field_2 from artist");
        selectMap.put( "Track", "select field_1, field_2 from track");
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        String statement = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return statement;

    }
}

That is, map all the statements with a Map. The "obvious" next step to this is to load the map from an external resource, such as a properties file, or a xml or even ( why not ) a database table, for extra flexibility.

This way you can keep your class clients ( and your self ) happy, because you don't needed "creating an instance" to do the work.

// Client usage:

...
List albums = ModelBase.findAll( Album.class );

...

Another approach is to create the instances from behind, and keep your client interface intact while using instance methods, the methods are marked as "protected" to avoid having external invocation. In a similar fashion of the previous sample you can also do this

// Second option, instance used under the hood.
class ModelBase {
    // Initialize the queries
    private static Map<String,ModelBase> daoMap = new HashMap<String,ModelBase>(); static {
        selectMap.put( "Album", new AlbumModel() );
        selectMap.put( "Artist", new ArtistModel());
        selectMap.put( "Track", new TrackModel());
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        ModelBase dao = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return dao.selectSql();
    }
    // Instance class to be overrided... 
    // this is "protected" ... 
    protected abstract String selectSql();
}
class AlbumModel  extends ModelBase {
    public String selectSql(){
        return "select ... from album";
    }
}
class ArtistModel  extends ModelBase {
    public String selectSql(){
        return "select ... from artist";
    }
}
class TrackModel  extends ModelBase {
    public String selectSql(){
        return "select ... from track";
    }
}

And you don't need to change the client code, and still have the power of polymorphism.

// Client usage:

...
List albums = ModelBase.findAll( Album.class ); // Does not know , behind the scenes you use instances.

...

I hope this helps.

A final note on using List vs. ArrayList. It is always better to program to the interface than to the implementation, this way you make your code more flexible. You can use another List implementation that is faster, or does something else, without changing your client code.

OscarRyz