Можем ли мы назначить пользовательские подсказки запроса для JPA NamedQueries

Мы должны добавлять номера запросов к каждому запросу, который выполняет наше приложение.

ПРИМЕР: SELECT * FROM... WHERE... QUERYNO 123456;

OpenJPA поддерживает подсказки запросов, но только для конкретных подсказок по конкретным реализациям.

...
Query q = em.createQuery("select m from Magazine m where ... ");
q.setHint("openjpa.hint.OptimizeResultCount", new Integer(2));
q.setHint("openjpa.FetchPlan.ReadLockMode","WRITE");
List r = q.getResultList();
...

Но в соответствии со спецификацией JPA и openjpa «Неверные подсказки или подсказки, которые не могут быть обработаны конкретной базой данных, игнорируются. В противном случае недопустимые подсказки приведут к возникновению исключения ArgumentException». ТАК указание «QUERYNO» в качестве подсказки, похоже, не имеет никакого значения.

Как создать пользовательскую подсказку для запроса, чтобы указать ее во время выполнения?

... Query q = em.createQuery ("выбрать m из журнала m, где..."); q.setHint("com.me.CustomQueryNoHint", new Integer(2234)); Список r = q.getResultList(); ...


person Eddie    schedule 08.11.2010    source источник
comment
+ 1 JPA2 все еще нуждается в некоторых готовых функциях ;)   -  person dira    schedule 20.11.2010
comment
см. часть ОБНОВЛЕННОГО ОТВЕТА в моем предыдущем ответе.   -  person dira    schedule 22.11.2010
comment
@ becomputer06 - Думаю, я не совсем ясно выражаю свою потребность. Ваше последнее решение применяется только к ведению журнала и не отправляется в базу данных. QUERYNO — это конкретное предложение, такое же, как WHERE или OPTIMIZE FOR X ROWS. publib.boulder.ibm.com/infocenter/dzichelp/v2r2/   -  person Eddie    schedule 24.11.2010


Ответы (3)


указание «QUERYNO» в качестве подсказки, похоже, не имеет никакого значения.

Правильный. Согласно документу, который вы процитировали, «QUERYNO» является недопустимой подсказкой, поэтому она игнорируется. Документ несколько сбивает с толку, но я считаю, что его можно интерпретировать в поддержку поведения, которое вы наблюдали. :-)

Как создать пользовательскую подсказку для запроса, чтобы указать ее во время выполнения?

Это гораздо более высокая задача. Я не верю, что OpenJPA был разработан для того, чтобы можно было писать собственные подсказки для запросов.

Я еще немного подумал о вашей реальной проблеме с желанием добавить определенную строку к КАЖДОМУ SQL, и я не думаю, что это было бы очень легко сделать в OpenJPA. Возможно, вы могли бы написать оболочку для вашего драйвера JDBC и добавить вашу строку к каждому SQL?

person Rick    schedule 16.11.2010
comment
Нужно ли указывать оболочку в конфигурации источника данных веб-контейнера, или я могу указать OpenJPA использовать оболочку с любым драйвером, который ей дает контейнер? Проблема в том, что наши администраторы баз данных разрешают только очень специфический драйвер JDBC и управляют его конфигурацией во всех средах, поэтому, если это не может быть сделано исключительно со стороны .war, это немедленно запрещено. - person Eddie; 16.11.2010

Вместо того, чтобы использовать JPQL и пытаться принудительно включить в запрос информацию, специфичную для поставщика, не рассматривали ли вы возможность написания собственного запроса, который сопоставляется с типом возвращаемого объекта?

em.createNativeQuery(YOUR_DB2_NATIVE_SQL_QUERY_STRING, Magazine.class)

Для вас будет больше работы, так как ваш собственный запрос должен выбирать значения столбцов, которые совпадают с сопоставленными столбцами в классе сущностей, но это должно работать. Когда вы используете собственный SQL, механизм запросов не должен анализировать и интерпретировать SQL, специфичный для поставщика, поэтому ваше специфичное для DB2 предложение в конце должно быть передано в базовую базу данных во время выполнения.

person Jim Tough    schedule 21.11.2010

Не полный ответ, а просто указатель...

Счетчик запросов

public class QueryCounter {
    private static long COUNTER = 0;

    private static long next() {
        return ++COUNTER;
    }

    private static String getHintValue() {
        return "/*Query No. " + next() + " */";
    }

    public static void setQueryCount(Query query) {
        /* EclipseLink */
        //query.setHint(QueryHints.HINT, getHintValue());
            query.setHint("eclipselink.sql.hint", getHintValue());

        /* OpenJPA + Oracle */
        //query.setHint("openjpa.hint.OracleSelectHint", getQueryHint());

        /* OpenJPA + MySQL */
        //query.setHint("openjpa.hint.MySQLSelectHin", getQueryHint());
    }
}

Использование

Organization sun = new Organization("Sun");
        em.persist(sun);
        tx.commit();
        Assert.assertNotNull(sun.getEntityId());

        Query query = em.createQuery("SELECT org.entityId FROM Organization org WHERE org.entityId = " + sun.getEntityId());
        QueryCounter.setQueryCount(query);
        query.getResultList();

        /*ServerSession does NOT log ReadObjectQuery??*/
        query = em.createQuery("SELECT org FROM Organization org WHERE org.entityId = " + sun.getEntityId());
        QueryCounter.setQueryCount(query);
        query.getResultList();

        query = em.createQuery("SELECT org.entityId FROM Organization org WHERE org.entityId = " + sun.getEntityId());
        QueryCounter.setQueryCount(query);
        query.getResultList();

Консоль

[EL Finest]: 2010-11-20 19:06:16.45--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReportQuery(referenceClass=Organization sql="SELECT entity_id FROM organization_tt WHERE (entity_id = ?)")
[EL Fine]: 2010-11-20 19:06:16.475--ServerSession(699542937)--Connection(1949550475)--Thread(Thread[main,5,main])--SELECT /*Query No. 1 */ entity_id FROM organization_tt WHERE (entity_id = ?)
    bind => [1]
[EL Finest]: 2010-11-20 19:06:23.372--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReadObjectQuery(referenceClass=Organization sql="SELECT entity_id, name FROM organization_tt WHERE (entity_id = ?)")
[EL Finest]: 2010-11-20 19:06:35.916--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReportQuery(referenceClass=Organization sql="SELECT entity_id FROM organization_tt WHERE (entity_id = ?)")
[EL Fine]: 2010-11-20 19:06:35.92--ServerSession(699542937)--Connection(1949550475)--Thread(Thread[main,5,main])--SELECT /*Query No. 3 */ entity_id FROM organization_tt WHERE (entity_id = ?)
    bind => [1]

OpenJPA имеет аналогичную концепцию 1.8 .7. Подсказки для конкретной базы данных. Посмотрите, помогут ли эти конкретные советы решить вашу задачу.

ОБНОВЛЕННЫЙ ОТВЕТ

@ Эдди, посмотри, может это тебе поможет...........

Фабрика пользовательских журналов

    public class MyLogFactory extends org.apache.openjpa.lib.log.LogFactoryImpl {

    /* copied from LogFactoryImpl.NEWLINE */
    private static final String NEWLINE = J2DoPrivHelper.getLineSeparator();

    private boolean sqlLogger;

    @Override
    public Log getLog(String channel) {
        if("openjpa.jdbc.SQL".equals(channel)) { // OR equalsIgnoreCase(channel) ??? 
            sqlLogger = true;
        }
        return super.getLog(channel);
    }

    @Override
    protected LogImpl newLogImpl() {
        if(sqlLogger) {
            sqlLogger = false; /* once an SQL Logger is created, we dont't need any more instances */
            return new LogImpl() {
                private long sqlCounter = 0;

                @Override
                protected String formatMessage(short level, String message, Throwable t) {
                    if(isSQLString(message)) {
                        StringBuffer formattedMessage = new StringBuffer(super.formatMessage(level, message, t));
                        StringBuffer queryNo = new StringBuffer();
                        queryNo.append("  [Query # ").append(++sqlCounter).append("]").append(NEWLINE);
                        formattedMessage.delete(formattedMessage.length() - NEWLINE.length(), formattedMessage.length());
                        formattedMessage.append(queryNo);
                        return formattedMessage.toString();
                    }
                    return super.formatMessage(level, message, t);
                }

                /* just a sample implementation; checks whether message contains the word "executing"
                 * more concrete implementation should check the message for SELECT, UPDATE, INSERT INTO, ALTER.... clauses */
                private boolean isSQLString(String message) {
                    if(message.contains("executing")) {
                        return true;
                    }
                    return false;
                }

            };
        }
        return super.newLogImpl();
    }

}

peristence.xml

<property name="openjpa.Log" value="org.opensource.logger.MyLogFactory(DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE)"/>

Тест

EntityManager em = Persistence.createEntityManagerFactory("default").createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Person person = new Person();
person.setName("Bond-OO7");
person.setAge(22);
em.persist(person);
tx.commit();
em.close();

Консоль

............
2084  default  TRACE  [main] openjpa.jdbc.SQL - <t 346613126, conn 1551158018> executing prepstmnt 556472773 SELECT SEQUENCE_OWNER AS SEQUENCE_SCHEMA, SEQUENCE_NAME FROM ALL_SEQUENCES  [Query # 1]
2136  default  TRACE  [main] openjpa.jdbc.SQL - <t 346613126, conn 1551158018> [52 ms] spent
2305  default  TRACE  [main] openjpa.jdbc.SQL - <t 346613126, conn 2026561073> executing prepstmnt 6637010 INSERT INTO Person (id, age, name) VALUES (?, ?, ?) [params=?, ?, ?]  [Query # 2]
2306  default  TRACE  [main] openjpa.jdbc.SQL - <t 346613126, conn 2026561073> [1 ms] spent
............

Справочник

person dira    schedule 20.11.2010
comment
Спасибо, но похоже, что вы просто добавляете комментарии между /* */, которые для OpenJPA можно использовать с помощью OracleHint. Однако QUERYNO — это не комментарий, а условие запроса DB2, оно должно быть в конце операторов и вне блоков комментариев. И, как указывает документация, на которую вы указываете, для OpenJPA подсказки используются только для Oracle и MySQL. Для моего словаря DB2 любая строка подсказки просто игнорируется. - person Eddie; 21.11.2010