用IN子句重写Hibernate Criteria,以便可以使用不同数量的IN子句重用相同的PreparedStatement

时间:2019-06-26 09:44:35

标签: sql hibernate hibernate-criteria

通过主键检索多条记录时,我会大量使用以下Hibernate查询

       Criteria c = session
                .createCriteria(Song.class)
                .setLockMode(LockMode.NONE)
                .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                .add(Restrictions.in("recNo", ids));
        List<Song> songs = c.list();

问题是ID的数量可以在1到50之间变化,并且每个ID的不同数量都需要不同的PreparedStatement。加上任何特定的预处理语句都与特定的数据库池连接相关联的事实,意味着重用PreparedStatement的机会非常低。

有没有办法我可以重写它,以便同一条语句可以与不同数量的in值一起使用,我想我读到某处可以通过使用ANY来完成,但找不到引用。

3 个答案:

答案 0 :(得分:1)

这称为“ in子句参数填充”,可以通过休眠属性激活:

<property
    name="hibernate.query.in_clause_parameter_padding"
    value="true"
</property>

在此处了解有关此主题的更多信息:https://vladmihalcea.com/improve-statement-caching-efficiency-in-clause-parameter-padding/

答案 1 :(得分:0)

在一些帮助下,我最终从Hibernate获得了一个普通的SQL连接,然后将标准SQL与ANY而不是IN一起使用。据我所知,使用ANY意味着每个连接只需要一个准备好的语句,因此比使用填充的IN更好。但是因为只使用SQL并没有太大用,如果您需要修改返回的数据

 public static List<SongDiff> getReadOnlySongDiffs(List<Integer> ids)
    {
        Connection connection = null;
        try
        {
            connection = HibernateUtil.getSqlSession();
            String SONGDIFFSELECT = "select * from SongDiff where recNo = ANY(?)";
            PreparedStatement ps = connection.prepareStatement(SONGDIFFSELECT);

            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);
        }
    }

 public static Connection getSqlSession() throws SQLException {
        if (factory == null || factory.isClosed()) {
            createFactory();
        }

        return ((C3P0ConnectionProvider)factory.getSessionFactoryOptions().getServiceRegistry().getService(C3P0ConnectionProvider.class)).getConnection();
    }

答案 2 :(得分:0)

如果您仍然使用旧版本的 Hibernate,如对 Simon's answer here 的评论中所建议,作为一种解决方法,您可以使用 jOOQ's ParsingConnection 通过应用 IN list padding feature在幕后透明。您可以像这样包装您的 DataSource

// Input DataSource ds1:
DSLContext ctx = DSL.using(ds1, dialect);
ctx.settings().setInListPadding(true);

// Use this DataSource for your code, instead:
DataSource ds2 = ctx.parsingDataSource();

I've written up a blog post to explain this more in detail here

(免责声明:我为 jOOQ 背后的公司工作)