Hacker News new | ask | show | jobs
by critium 3807 days ago
Sorry if I did too much TL;DR but this reads very much like Apache Torque, which has been around for a very loooooooong time.

https://db.apache.org/torque/torque-4.0/index.html

Apache Torque is an object-relational mapper for java. In other words, Torque lets you access and manipulate data in a relational database using java objects. Unlike most other object-relational mappers, Torque does not use reflection to access user-provided classes, but it generates the necessary classes (including the Data Objects) from an XML schema describing the database layout. The XML file can either be written by hand or a starting point can be generated from an existing database. The XML schema can also be used to generate and execute a SQL script which creates all the tables in the database.

As Torque hides database-specific implementation details, Torque makes an application independent of a specific database if no exotic features of the database are used.

Usage of code generation eases the customization of the database layer, as you can override the generated methods and thus easily change their behavior. A modularized template structure allows inclusion of your own code generation templates during the code generation process.

1 comments

They are not the same and probably many modern ORM's share more in common with jOOQ than Torgue has in common with jOOQ. An example would be schema generation. jOOQ is reverse schema based. You make your schema with whatever schema evolution tools you like (Flyway is the most common) and then run the code generator. Torque is the opposite (notice they say starting point not final) not to mention its also XML based.

Oh and jOOQ has an extremely powerful DSL that is close to 1-1 with SQL and whole bunch of reflection based conversion abilities (including dotted path data binding which I am happy to say I inspired the author of jOOQ to add :) )

Torque does code-gen as well. This was before any fancy tools so we basically just had hand generated DDL.

https://db.apache.org/torque/torque-4.0/documentation/orm-re...

Regen, then fix compile issues. Thats how I used it a long long (10 years?) time ago.

jOOQ DSL is nice, this is just as readable(ABC is a generated class)

  Criteria crit = new Criteria()
    .where(ABC.A, 1, Criteria.LESS_THAN)
    .and(ABC.B, 2, Criteria.GREATER_THAN)
    .or(ABC.A, 5, Criteria.GREATER_THAN);
Working with XML, not the nicest thing, i do agree, but also not the worst thing.

Not a flag-bearer in any way, but I just wanted to point out that if code-gen db access is your thing, there are also other tools to consider. It most certainly had its warts. Like issues handling complicated joins. Or more seriously, whos maintaining it. Its been a long time since i've look at it with any seriousness, not sure if they have fixed them or not.

edit: formatting

Here's a jOOQ example from the "PostgresDatabase" class, which is used to reverse-engineer the meta data. How would you write it in Torque?

    .select()
    .from(
         select(
            TABLES.TABLE_SCHEMA,
            TABLES.TABLE_NAME,
            TABLES.TABLE_NAME.as("specific_name"),
            inline(false).as("table_valued_function"),
            inline(false).as("materialized_view"),
            PG_DESCRIPTION.DESCRIPTION)
        .from(TABLES)
        .join(PG_NAMESPACE)
            .on(TABLES.TABLE_SCHEMA.eq(PG_NAMESPACE.NSPNAME))
        .join(PG_CLASS)
            .on(PG_CLASS.RELNAME.eq(TABLES.TABLE_NAME))
            .and(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
        .leftOuterJoin(PG_DESCRIPTION)
            .on(PG_DESCRIPTION.OBJOID.eq(oid(PG_CLASS)))
            .and(PG_DESCRIPTION.OBJSUBID.eq(0))
        .where(TABLES.TABLE_SCHEMA.in(getInputSchemata()))
    
        // To stay on the safe side, if the INFORMATION_SCHEMA ever
        // includs materialised views, let's exclude them from here
        .and(row(TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME).notIn(
            select(
                PG_NAMESPACE.NSPNAME,
                PG_CLASS.RELNAME)
            .from(PG_CLASS)
            .join(PG_NAMESPACE)
                .on(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
            .where(PG_CLASS.RELKIND.eq(inline("m")))
        ))
    
    // [#3254] Materialised views are reported only in PG_CLASS, not
    //         in INFORMATION_SCHEMA.TABLES
    .unionAll(
        select(
            PG_NAMESPACE.NSPNAME,
            PG_CLASS.RELNAME,
            PG_CLASS.RELNAME,
            inline(false).as("table_valued_function"),
            inline(true).as("materialized_view"),
            PG_DESCRIPTION.DESCRIPTION)
        .from(PG_CLASS)
        .join(PG_NAMESPACE)
            .on(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
        .leftOuterJoin(PG_DESCRIPTION)
            .on(PG_DESCRIPTION.OBJOID.eq(oid(PG_CLASS)))
            .and(PG_DESCRIPTION.OBJSUBID.eq(0))
        .where(PG_NAMESPACE.NSPNAME.in(getInputSchemata()))
        .and(PG_CLASS.RELKIND.eq(inline("m"))))
    
    // [#3375] [#3376] Include table-valued functions in the set of tables
    .unionAll(
        tableValuedFunctions()
    
        ?   select(
                ROUTINES.ROUTINE_SCHEMA,
                ROUTINES.ROUTINE_NAME,
                ROUTINES.SPECIFIC_NAME,
                inline(true).as("table_valued_function"),
                inline(false).as("materialized_view"),
                inline(""))
            .from(ROUTINES)
            .join(PG_NAMESPACE).on(ROUTINES.SPECIFIC_SCHEMA.eq(PG_NAMESPACE.NSPNAME))
            .join(PG_PROC).on(PG_PROC.PRONAMESPACE.eq(oid(PG_NAMESPACE)))
                          .and(PG_PROC.PRONAME.concat("_").concat(oid(PG_PROC)).eq(ROUTINES.SPECIFIC_NAME))
            .where(ROUTINES.ROUTINE_SCHEMA.in(getInputSchemata()))
            .and(PG_PROC.PRORETSET)
    
        :   empty)
    .asTable("tables"))
    .orderBy(1, 2)
    .fetch()) {
That was a great steal. Thanks again for the idea, Adam! :)