Могу ли я использовать ЛЮБОЙ или Массив для альтернативы переменной IN в Hibernate 4.3.11 и H2 1.4.199? - PullRequest
0 голосов
/ 08 октября 2019

Используя Java 8, HIbernate 4.3.11 и H2 1.4.199

Я создаю html-отчет, состоящий из многих страниц, и мне нужно сделать большее количество запросов просто с помощью предложения where на основеВ вызове на основе первичного ключа. Если количество значений в предложении IN варьируется, то необходимо подготовить другой подготовленный запрос, существующие нельзя использовать повторно. Поэтому я пытаюсь уменьшить потребность в таком количестве подготовленных операторов.

Это решение в Hibernate 5, которое называется hibernate.query.in_clause_parameter_padding , как описано в https://vladmihalcea.com/improve-statement-caching-efficiency-in-clause-parameter-padding/

Однако я не могу перейти на Hibernate 5, и кажется, что это решение было заменено использованием ЛЮБОГО предложения или ВСЕГДА. Но я еще не смог получить этот рабочий

getSongDiffs1 () - это мой первоначальный запрос на основе критериев Hibernate, getSongDiffs2 () - это запрос, переписанный для использования нативного sqlработает, но не имеет никакого преимущества в производительности, затем я попытался использовать ЛЮБОЙ для getSongDiffs2 () и ARRAYS с getSongDiffs () , но это приводило к ошибкам при подготовке операторов

Хотя япытаюсь использовать собственный SQL, потому что сгенерированный из сеанса Hibernate я работаю с org.hibernate.SQLQuery вместо Connection , поэтому не смог полностью следовать решению, помеченному решением Postgres при заданном предложении PreparedStatement INальтернативы?

  public static List<SongDiff> getSongDiffs1(StatelessSession session, List<Integer> ids)
    {
        try
        {
            Criteria c = session
                    .createCriteria(SongDiff.class)
                    .add(Restrictions.in("recNo", ids));
            List<SongDiff> songDiffs = c.list();
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public static List<SongDiff> getSongDiffs2(StatelessSession session, List<Integer> ids)
    {
        try
        {
            SQLQuery q = session.createSQLQuery("select * from SongDiff where recNo in :recNos");
            q.addEntity(SongDiff.class);
            q.setParameterList("recNos", ids);
            List<SongDiff> songDiffs = q.list();
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public static List<SongDiff> getSongDiffs3(StatelessSession session, List<Integer> ids)
    {
        try
        {
            SQLQuery q = session.createSQLQuery("select * from SongDiff where recNo = ANY(:recNos)");
            q.addEntity(SongDiff.class);
            q.setParameterList("recNos", ids);
            List<SongDiff> songDiffs = q.list();
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public static List<SongDiff> getSongDiffs(StatelessSession session, List<Integer> ids)
    {
        try
        {
            SQLQuery q = session.createSQLQuery("select * from SongDiff where recNo in :recNos)");
            q.addEntity(SongDiff.class);
            q.setParameter("recNos", ids.toArray());
            List<SongDiff> songDiffs = q.list();
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

Дает ошибку

Вызывается: org.h2.jdbc.JdbcSQLSyntaxErrorException: Синтаксическая ошибка в операторе SQL "SELECT * FROM SONGDIFF, ГДЕ RECNO IN?? [*]) ";Ожидаемый "("; SQL-оператор:

На основе ответов, проб и ошибок ниже, я написал это

 public static List<SongDiff> getSongDiffs(StatelessSession session, List<Integer> ids)
    {
        try
        {
            StringBuilder sb = new StringBuilder();
            for(Integer next:ids)
            {
                sb.append(next +",");
            }
            sb.setLength(sb.length() -1);

            SQLQuery q = session.createSQLQuery("select * from SongDiff where recNo IN (SELECT * FROM UNNEST("+sb.toString()+"))");
            q.addEntity(SongDiff.class);
            List<SongDiff> songDiffs = q.list();
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

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

08/10/2019 16.49.40:BST:DEBUG: select * from SongDiff where recNo IN (SELECT * FROM UNNEST(23,24,25,26,27,28,29,30,31,32,33,34,35))
08/10/2019 16.49.40:BST:DEBUG: select * from SongDiff where recNo IN (SELECT * FROM UNNEST(32,33,34,35,23,24,25,26,27,28,29,30,31))

Так что я предполагаю, что это означает, что они являются отдельными подготовленными утверждениями, что сводит на нет смысл этого, поэтому возможно ли заставить это работать с setParamter ()

Используя последние рекомендации, кажется, что ЛЮБОЙ и UNNEST варианты отправляют действительный SQL

public static List<SongDiff> getSongDiffs(StatelessSession session, List<Integer> ids)
{
    try
    {
        StringBuilder sb = new StringBuilder();
        for(Integer next:ids)
        {
            sb.append(next +",");
        }
        sb.setLength(sb.length() -1);

        SQLQuery q = session.createSQLQuery("select * from SongDiff where recNo = ANY(:recNos)");
        q.addEntity(SongDiff.class);
        q.setParameter("recNos", ids.toArray(new Integer[10]));
        List<SongDiff> songDiffs = q.list();
        return songDiffs;
    }
    catch (Exception e)
    {
        MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
        throw new RuntimeException(e);
    }
}

, но теперь возникает проблема при извлечении ResultSet

09/10/2019 08.02.14:BST:SongChangesCache:getSongDiffs:SEVERE: Failed to get SongDiffsFromDb:could not extract ResultSet
org.hibernate.exception.DataException: could not extract ResultSet
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:69)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:91)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2066)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1863)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839)
    at org.hibernate.loader.Loader.doQuery(Loader.java:910)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:355)
    at org.hibernate.loader.Loader.doList(Loader.java:2554)
    at org.hibernate.loader.Loader.doList(Loader.java:2540)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2370)
    at org.hibernate.loader.Loader.list(Loader.java:2365)
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:353)
    at org.hibernate.internal.StatelessSessionImpl.listCustomQuery(StatelessSessionImpl.java:741)
    at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:311)
    at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:141)
    at com.jthink.songkong.db.SongChangesCache.getSongDiffs(SongChangesCache.java:407)
    at com.jthink.songkong.reports.utils.HtmlAlbumArtistAlbumGridCreator.createAlbumFilesHtmlPage(HtmlAlbumArtistAlbumGridCreator.java:350)
    at com.jthink.songkong.reports.utils.HtmlAlbumArtistAlbumGridCreator.createAlbumsHtmlPage(HtmlAlbumArtistAlbumGridCreator.java:282)
    at com.jthink.songkong.reports.utils.HtmlAlbumArtistAlbumGridCreator.createArtistsHtmlPage(HtmlAlbumArtistAlbumGridCreator.java:198)
    at com.jthink.songkong.reports.utils.HtmlAlbumArtistAlbumGridCreator.<init>(HtmlAlbumArtistAlbumGridCreator.java:75)
    at com.jthink.songkong.reports.AbstractByMetadataSection.outputSection(AbstractByMetadataSection.java:306)
    at com.jthink.songkong.reports.fixsongsreport.ByArtistAlbumSection.call(ByArtistAlbumSection.java:34)
    at com.jthink.songkong.reports.fixsongsreport.ByArtistAlbumSection.call(ByArtistAlbumSection.java:19)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "X'aced0005757200145b4c6a6176612e6c616e672e496e74656765723bfe97ada00183e21b02000078700000000d737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b0200007870000000177371007e0002000000187371007e0002000000197371007e00020000001a7371007e00020000001b7371007e00020000001c7371007e00020000001d7371007e00020000001e7371007e00020000001f7371007e0002000000207371007e0002000000217371007e0002000000227371007e000200000023' (SONGDIFF: ""RECNO"" INTEGER NOT NULL)"; SQL statement:
select * from SongDiff where recNo = ANY(?) [22018-199]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:455)
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:427)
    at org.h2.message.DbException.get(DbException.java:194)
    at org.h2.table.Column.convert(Column.java:204)
    at org.h2.table.Column.convert(Column.java:185)
    at org.h2.index.IndexCondition.getCurrentValueList(IndexCondition.java:152)
    at org.h2.index.IndexCursor.prepare(IndexCursor.java:96)
    at org.h2.index.IndexCursor.find(IndexCursor.java:154)
    at org.h2.table.TableFilter.next(TableFilter.java:475)
    at org.h2.command.dml.Select$LazyResultQueryFlat.fetchNextRow(Select.java:1882)
    at org.h2.result.LazyResult.hasNext(LazyResult.java:101)
    at org.h2.result.LazyResult.next(LazyResult.java:60)
    at org.h2.command.dml.Select.queryFlat(Select.java:742)
    at org.h2.command.dml.Select.queryWithoutCache(Select.java:884)
    at org.h2.command.dml.Query.queryWithoutCacheLazyCheck(Query.java:151)
    at org.h2.command.dml.Query.query(Query.java:435)
    at org.h2.command.dml.Query.query(Query.java:397)
    at org.h2.command.CommandContainer.query(CommandContainer.java:145)
    at org.h2.command.Command.executeQuery(Command.java:202)
    at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:115)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:116)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)
    ... 25 more

Так что я сейчасесть способ использовать собственный объект Connection, поэтому можно попробовать стандартный способ JDBC

 public static List<SongDiff> getSongDiffs(StatelessSession session, List<Integer> ids)
    {
        try
        {
            Connection connection = HibernateUtil.getSqlSession();
            PreparedStatement ps = connection.prepareStatement("select * from SongDiff where recNo = ANY(?)");
            ps.setArray(1, connection.createArrayOf("Integer",  ids.toArray(new Integer[10])));
            ResultSet rs = ps.executeQuery();
            while(rs.next())
            {
                MainWindow.logger.severe(rs.getString("recNo"));
            }
            return null;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

, но это не удается с

java.lang.AbstractMethodError: com.mchange.v2. c3p0.impl.NewProxyConnection.createArrayOf (Ljava / lang / String; [Ljava / lang / Object;) Ljava / sql / Array;

Не уверен, если это просто означает, что c3p0 не поддерживает это (но h2делает). Однако независимо я не могу обновить c3p0 до последней версии, но есть ли другой способ создания массива, который можно передать в метод setArray ()?

Обновление решения Используя этот метод для получения соединения jdbc и ответ в Evenijs, у меня теперь есть следующий метод работы.

public static List<SongDiff> getSongDiffs(List<Integer> ids)
    {
        Connection connection = null;
        try
        {
            connection = HibernateUtil.getSqlSession();
            PreparedStatement ps = connection.prepareStatement("select * from SongDiff where recNo = ANY(?)");
            ps.setObject(1,  ids.toArray(new Integer[ids.size()]));
            ResultSet rs = ps.executeQuery();
            List<SongDiff> songDiffs = new ArrayList<>(ids.size());
            while(rs.next())
            {
                SongDiff sd = new SongDiff();
                sd.setRecNo(rs.getInt("recNo"));
                sd.setDiff(rs.getBytes("diff"));
                songDiffs.add(sd);
            }
            return songDiffs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed to get SongDiffsFromDb:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
        finally
        {
            SessionUtil.close(connection);
        }
    }

1 Ответ

1 голос
/ 08 октября 2019

С JDBC вы можете использовать условие … = ANY(?) или … IN(UNNEST(?)) в последних версиях H2 (в старых версиях вместо этого можно использовать функцию TABLE(), как описано в их документации). H2 ожидает массив в качестве аргумента (Object[] или java.sql.Array).

Вы также сможете использовать эту функцию с собственным SQL в Hibernate.

...