Cayenne
  1. Cayenne
  2. CAY-1287

Enchance SQLTemplate API and it's Documentation for forming not-mapping (DataRow) queries

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 2.0 branch
    • Fix Version/s: 3.1 (final)
    • Component/s: Core Library, Documentation
    • Labels:
      None

      Description

      I wish to use nice SQLTemplate scripting engine for my simple not-mapping (DataRow) runtime-formed queries.
      But now I should pass a DataObject to SQLTemplate constructor.
      The question is: "For what?"
      To do mapping???
      If my query is like: "SELECT max(id) as MAX_ID from Table1" it has no sense!

      I want to add constructor like: SQLTemplate(String defaultTemplate, Map parameter)
      and teach DataContext to perform such queries well.

        Activity

        Hide
        Evgeny Ryabitskiy added a comment - - edited

        So... why I set it to bug and not to a new feature....

        If you look at Javadoc of SQLTemplate constructor, I can see:

        public SQLTemplate(Class rootClass, String defaultTemplate)

        { setDefaultTemplate(defaultTemplate); setRoot(rootClass); }

        So lets look at setRoot(rootClass) method:

        /**

        • Sets the root of the query
        • @param value The new root
        • @throws IllegalArgumentException if value is not a String, ObjEntity, DbEntity,
        • Procedure, DataMap, Class or null. < — look here!
          */
          public void setRoot(Object value) {...... }

        But (!!!) if I pass null it throws exception! Looks like it was expected before to work without mapping for queries....
        but implementation not follows it....

        Show
        Evgeny Ryabitskiy added a comment - - edited So... why I set it to bug and not to a new feature.... If you look at Javadoc of SQLTemplate constructor, I can see: public SQLTemplate(Class rootClass, String defaultTemplate) { setDefaultTemplate(defaultTemplate); setRoot(rootClass); } So lets look at setRoot(rootClass) method: /** Sets the root of the query @param value The new root @throws IllegalArgumentException if value is not a String, ObjEntity, DbEntity, Procedure, DataMap, Class or null. < — look here! */ public void setRoot(Object value) {...... } But (!!!) if I pass null it throws exception! Looks like it was expected before to work without mapping for queries.... but implementation not follows it....
        Hide
        Andrus Adamchik added a comment -

        Evgeny, I don't think this is an issue at all. Indeed SQLTemplate can be used without a root entity (or a Java class) attached to it. However during execution we need to know which DataNode to run it against (the mapping can have more than one DataNode). So we still need that piece of information. And this is achieved by using the following constructor:

        SQLTemplate(DataMap rootMap, String defaultTemplate)

        Where rootMap is the DataMap that is linked to the desired DataNode. This should solve your problem I think.

        Show
        Andrus Adamchik added a comment - Evgeny, I don't think this is an issue at all. Indeed SQLTemplate can be used without a root entity (or a Java class) attached to it. However during execution we need to know which DataNode to run it against (the mapping can have more than one DataNode). So we still need that piece of information. And this is achieved by using the following constructor: SQLTemplate(DataMap rootMap, String defaultTemplate) Where rootMap is the DataMap that is linked to the desired DataNode. This should solve your problem I think.
        Hide
        Evgeny Ryabitskiy added a comment -

        Ah yeah... I found way to run SQLTemplate from runtime without objectMapping! wow... It was not easy... really...

        But I don't see why we need a DomainMap? We could pass DataNode and it should be enough...
        maybe we can create constructor like:
        SQLTemplate(DataNode dataNode, String defaultTemplate)
        and few more things... why we need queryWithParameters...

        Now to run code SQLTemplate I should write follow code:

        String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)";
        Configuration config = Configuration.getSharedConfiguration();
        DataDomain domain = config.getDomain();
        DataMap anyDataMap = (DataMap) domain.getNode("MyNode").getDataMaps().iterator().next();
        SQLTemplate query = new SQLTemplate(anyDataMap , sql);
        Map parameters = new HashMap();
        parameters.put("Name", "Eugene");
        SQLTemplate newQuery = query.queryWithParameters(parameters);
        List rowList = ctx.performQuery(newQuery);

        It's huge and contains lot's of redundant operations... And it's hard to find this solution! We should add it to user guide....
        We can do some Improvement to white this code:

        String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)";
        Configuration config = Configuration.getSharedConfiguration();
        DataDomain domain = config.getDomain();
        Map parameters = new HashMap();
        parameters.put("Name", "Eugene");
        //here is new constructor: SQLTemplate(DataNode dataNode, String defaultTemplate, Map parameters)
        SQLTemplate query = new SQLTemplate(domain.getNode("MyNode"), sql, parameters);
        List rowList = ctx.performQuery(newQuery);

        It's more intuitive thing and doesn't requires redundant things!

        Or I have alternative solution to make this code executable:

        String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)";
        Configuration config = Configuration.getSharedConfiguration();
        DataDomain domain = config.getDomain();
        Map parameters = new HashMap();
        parameters.put("Name", "Eugene");
        //here is new constructor: SQLTemplate(DataNode dataNode, String defaultTemplate)
        SQLTemplate query = new SQLTemplate(domain.getNode("MyNode"), sql, parameters);
        // pass parameters while performQuery to every query (not only while performing NamedQuery)
        List rowList = ctx.performQuery(newQuery, parameters);

        Also more comfortable thing...

        Show
        Evgeny Ryabitskiy added a comment - Ah yeah... I found way to run SQLTemplate from runtime without objectMapping! wow... It was not easy... really... But I don't see why we need a DomainMap? We could pass DataNode and it should be enough... maybe we can create constructor like: SQLTemplate(DataNode dataNode, String defaultTemplate) and few more things... why we need queryWithParameters... Now to run code SQLTemplate I should write follow code: String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)"; Configuration config = Configuration.getSharedConfiguration(); DataDomain domain = config.getDomain(); DataMap anyDataMap = (DataMap) domain.getNode("MyNode").getDataMaps().iterator().next(); SQLTemplate query = new SQLTemplate(anyDataMap , sql); Map parameters = new HashMap(); parameters.put("Name", "Eugene"); SQLTemplate newQuery = query.queryWithParameters(parameters); List rowList = ctx.performQuery(newQuery); It's huge and contains lot's of redundant operations... And it's hard to find this solution! We should add it to user guide.... We can do some Improvement to white this code: String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)"; Configuration config = Configuration.getSharedConfiguration(); DataDomain domain = config.getDomain(); Map parameters = new HashMap(); parameters.put("Name", "Eugene"); //here is new constructor: SQLTemplate(DataNode dataNode, String defaultTemplate, Map parameters) SQLTemplate query = new SQLTemplate(domain.getNode("MyNode"), sql, parameters); List rowList = ctx.performQuery(newQuery); It's more intuitive thing and doesn't requires redundant things! Or I have alternative solution to make this code executable: String sql = "SELECT * FROM PersonTable WHERE Name = #bind($Name)"; Configuration config = Configuration.getSharedConfiguration(); DataDomain domain = config.getDomain(); Map parameters = new HashMap(); parameters.put("Name", "Eugene"); //here is new constructor: SQLTemplate(DataNode dataNode, String defaultTemplate) SQLTemplate query = new SQLTemplate(domain.getNode("MyNode"), sql, parameters); // pass parameters while performQuery to every query (not only while performing NamedQuery) List rowList = ctx.performQuery(newQuery, parameters); Also more comfortable thing...
        Hide
        Evgeny Ryabitskiy added a comment -

        So the idea of this issue is to enhance API to make it more intuitive....

        Show
        Evgeny Ryabitskiy added a comment - So the idea of this issue is to enhance API to make it more intuitive....
        Hide
        Andrus Adamchik added a comment -

        > But I don't see why we need a DomainMap? We could pass DataNode and it should be enough...
        > maybe we can create constructor like:
        > SQLTemplate(DataNode dataNode, String defaultTemplate)

        -1. Cayenne has layered structure and the bottom layers are intentionally isolated from the top layers.

        Show
        Andrus Adamchik added a comment - > But I don't see why we need a DomainMap? We could pass DataNode and it should be enough... > maybe we can create constructor like: > SQLTemplate(DataNode dataNode, String defaultTemplate) -1. Cayenne has layered structure and the bottom layers are intentionally isolated from the top layers.
        Hide
        Andrus Adamchik added a comment -

        One example why this is a bad idea is because SQLTemplate is used in ROP, which has no notion of DataNode.

        Show
        Andrus Adamchik added a comment - One example why this is a bad idea is because SQLTemplate is used in ROP, which has no notion of DataNode.
        Hide
        Evgeny Ryabitskiy added a comment -

        But it's also very confusing to developer course he don't know which Map to Use and User Guide have nothing about it.... We should write about it there!

        I have simple solution for it.. At least it's not confusing to new developer.... :

        // new constructor using only DataNode
        public SQLTemplate(DataNode dataNode, String defaultTemplate)

        { //use any DomainMap course there is no metter... this(dataNode.getDomainMaps.iterator().next(), defaultTemplate); }
        Show
        Evgeny Ryabitskiy added a comment - But it's also very confusing to developer course he don't know which Map to Use and User Guide have nothing about it.... We should write about it there! I have simple solution for it.. At least it's not confusing to new developer.... : // new constructor using only DataNode public SQLTemplate(DataNode dataNode, String defaultTemplate) { //use any DomainMap course there is no metter... this(dataNode.getDomainMaps.iterator().next(), defaultTemplate); }
        Hide
        Evgeny Ryabitskiy added a comment -

        I have changed issue description to be more accurate...

        Show
        Evgeny Ryabitskiy added a comment - I have changed issue description to be more accurate...
        Hide
        Andrey Razumovsky added a comment -

        3.0 cannot have API changes, moving to 3.1

        By the way, Evgeny, as Andrus mentioned, classes from o.a.c.query package (any many others, like o.a.c.map) cannot have reference to classes from o.a.c.access because last ones do not exist in cayenne-client jar and not avaliable on ROP

        Show
        Andrey Razumovsky added a comment - 3.0 cannot have API changes, moving to 3.1 By the way, Evgeny, as Andrus mentioned, classes from o.a.c.query package (any many others, like o.a.c.map) cannot have reference to classes from o.a.c.access because last ones do not exist in cayenne-client jar and not avaliable on ROP

          People

          • Assignee:
            Unassigned
            Reporter:
            Evgeny Ryabitskiy
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:

              Development