[logback-dev] [JIRA] Created: (LBCLASSIC-188) Make table andcolumn names overridable

Durchholz, Joachim Joachim.Durchholz at hennig-fahrzeugteile.de
Fri Feb 26 16:03:30 CET 2010


Just a general note:

It think that the number and semantics of columns should be defined in
the same class that actually fills them via stmt.setXxx calls. I guess
that's what got me thinking about improvement proposals in the first
place.

I see three ways to accomplish this:
1. Subclass DBAppender.
2. Use a DBWriter class that logs whatever information is thrown at it.
This means generics, because the "whatever information" depends on the
kind of appender used but can be statically determined for each
appender. (More details on the nature of generics below.)
3. Use a DBNameResolver class that provides column names. The case for
generics is the same: different appenders will want to access different
but statically predetermined sets of columns.

> I wish I had a good and short explanation why generics do not 
> make sense here. As mentioned a little earlier, 
> DBNameResolver provides a service to DBAppender. DBAppender 
> has a DBNameResolver but does not store a collection of 
> DBNameResolvers or process DBNameResolvers.

Making a class generic does not mean that it "contains" or "processes"
objects of the parameter class. java.lang.Comparable is the classical
counterexample to such interpretations of genericity.
Generics are a means of consistently varying a type across result and
parameter positions within a class or class sub-hierarchy. This can be
used to make containers, but there are many more use cases than that.

> Besides that, generics in Java are quite clumsy to use.

Agreed.
Code completion does a lot to alleviate the pain, and getting used to
the way that generics look like does help, too.

> More to the point 
> and as you probably know already, generics get erased (type 
> erasure) as explained in
> 
>    http://java.sun.com/docs/books/tutorial/java/generics/erasure.html

That does not affect the case at hand. As long as the code gets run
through a Java compiler, you will still get static guarantees that your
code does not violate the type constraint.
The JVM will erase the type parameter and hence cannot enforce the
constraints at class load time, plus type erasure will prevent some type
checks at the Java level, but this is not the case for the code I
proposed.
(Also, type erasure is schedules for removal. I hope it will go away
with Java 7.)

> One cannot do much with java generics really.

My personal experience has been otherwise.
I routinely write generics to good effect.

There is a learning curve.
I worked from an Eiffel background, which is a bit less refined about
type bounds than Java, and it took me around a week to wrap my mind
about the basic workings and another week to pick up some fine points.
I reaped benefits, too. I have converted some of my code to generics,
and I found several cases where type casts and string discriminators
that I had considered statically safe were, in face, unsafe and just
waiting for the rare occasion to throw an exception. Equivalently, I got
rid of a lot of "else { throw new RuntimeException ("This can't
happen"); }" type of code.

YMMV :)

----

> Anyway, what's your second idea?

I'd simply use overridable functions in DBAppender to provide the SQL
for database access.

So:

class DBAppender {
  protected String eventTableName() { return "event_log"; }
  protected String eventTimestampColumnName () { return "timestmp"; }
  ...
  protected String insertPropertiesSql;
  protected void subAppend(...) {
    if (insertPropertiesSql == null) {
      insertPropertiesSql =
        "INSERT INTO " + eventTableName() + "("
        + eventTimestampColumnName() + ", "
        ...
    }
    ... // existing code goes here
  }
}

Subclasses can easily override the table and column names to their
taste.
(Sounds simpler to me than any of the alternatives. DBAppender should
not compute anything, it should be a thin layer that just spits out the
data that it's given. It shouldn't even be merging properties, it should
be given pre-merged property sets by a caller... or an object that does
the merge on request. Hey, LoggingEvent could do that, all input for
mergePropertyMaps is from a LoggingEvent anyway.) (Oops. That went off
on a tangent. Sorry for that.)

Regards,
Jo


More information about the logback-dev mailing list