SQLite + Mybatis-guice 抛出 ArrayIndexOutOfBoundsException

SQLite + Mybatis-guice throw ArrayIndexOutOfBoundsException

我正在尝试实现一个 Guice 模块,让 Guacamole use SQLite as a backend. The Guacamole project has a generic JDBC base module. This lets you implement modules for specific datastores with less code. Most of the lines of code end up being in mapper XML files. The project provides PostgreSQL and MySQL implementations

我将这个 SQLite 模块基于 MySQL 模块。对于映射器 XML 文件,SQLite 和 MySQL 非常相似,我不必进行任何更改。但是,当我尝试使用 SQLite 模块时,出现此错误:

### Error querying database.  Cause: java.lang.ArrayIndexOutOfBoundsException: 2
### The error may exist in org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT             guacamole_connection_group.connection_group_id,             connection_group_name,             parent_id,             type,             max_connections,             max_connections_per_user,             enable_session_affinity         FROM guacamole_connection_group         JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id         WHERE guacamole_connection_group.connection_group_id IN              (                   ?              )              AND user_id = ?             AND permission = 'READ';          SELECT parent_id, guacamole_connection_group.connection_group_id         FROM guacamole_connection_group         JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id         WHERE parent_id IN              (                   ?              )              AND user_id = ?             AND permission = 'READ';          SELECT parent_id, guacamole_connection.connection_id         FROM guacamole_connection         JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id         WHERE parent_id IN              (                   ?              )              AND user_id = ?             AND permission = 'READ';
### Cause: java.lang.ArrayIndexOutOfBoundsException: 2

看起来问题在于向查询传递了两个参数,但每个参数都重复了三次。 MyBatis 在生成 PreparedStatement 时,就好像有6个参数需要传入

这是它有问题的查询:

<!-- Select multiple connection groups by identifier only if readable -->
<select id="selectReadable" resultMap="ConnectionGroupResultMap"
        resultSets="connectionGroups,childConnectionGroups,childConnections">

    SELECT
        guacamole_connection_group.connection_group_id,
        connection_group_name,
        parent_id,
        type,
        max_connections,
        max_connections_per_user,
        enable_session_affinity
    FROM guacamole_connection_group
    JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
    WHERE guacamole_connection_group.connection_group_id IN
        <foreach collection="identifiers" item="identifier"
                 open="(" separator="," close=")">
            #{identifier,jdbcType=VARCHAR}
        </foreach>
        AND user_id = #{user.objectID,jdbcType=INTEGER}
        AND permission = 'READ';

    SELECT parent_id, guacamole_connection_group.connection_group_id
    FROM guacamole_connection_group
    JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
    WHERE parent_id IN
        <foreach collection="identifiers" item="identifier"
                 open="(" separator="," close=")">
            #{identifier,jdbcType=VARCHAR}
        </foreach>
        AND user_id = #{user.objectID,jdbcType=INTEGER}
        AND permission = 'READ';

    SELECT parent_id, guacamole_connection.connection_id
    FROM guacamole_connection
    JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
    WHERE parent_id IN
        <foreach collection="identifiers" item="identifier"
                 open="(" separator="," close=")">
            #{identifier,jdbcType=VARCHAR}
        </foreach>
        AND user_id = #{user.objectID,jdbcType=INTEGER}
        AND permission = 'READ';

</select>

如果我手动填充参数,我可以对 SQLite 数据库执行此操作。此外,MySQL 版本工作正常。

这到底是怎么回事?我能做些什么来调试这个?是 MyBatis 问题还是 JDBC 连接器的问题?

如果有帮助,您可以查看模块 here 的代码。

这是与此查询相关的映射器参数的方法。 ConnectionGroup 的完整映射器 类 是 here and here. The full mapper XML for my SQLite module is here.

Collection<ModelType> selectReadable(@Param("user") UserModel user,
        @Param("identifiers") Collection<String> identifiers);

这是 ConnectionGroupResultMap 的样子:

<resultMap id="ConnectionGroupResultMap" type="org.apache.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" >

    <!-- Connection group properties -->
    <id     column="connection_group_id"      property="objectID"               jdbcType="INTEGER"/>
    <result column="connection_group_name"    property="name"                   jdbcType="VARCHAR"/>
    <result column="parent_id"                property="parentIdentifier"       jdbcType="INTEGER"/>
    <result column="type"                     property="type"                   jdbcType="VARCHAR"
            javaType="org.apache.guacamole.net.auth.ConnectionGroup$Type"/>
    <result column="max_connections"          property="maxConnections"         jdbcType="INTEGER"/>
    <result column="max_connections_per_user" property="maxConnectionsPerUser"  jdbcType="INTEGER"/>
    <result column="enable_session_affinity"  property="sessionAffinityEnabled" jdbcType="BOOLEAN"/>

    <!-- Child connection groups -->
    <collection property="connectionGroupIdentifiers" resultSet="childConnectionGroups" ofType="java.lang.String"
                column="connection_group_id" foreignColumn="parent_id">
        <result column="connection_group_id"/>
    </collection>

    <!-- Child connections -->
    <collection property="connectionIdentifiers" resultSet="childConnections" ofType="java.lang.String"
                column="connection_group_id" foreignColumn="parent_id">
        <result column="connection_id"/>
    </collection>

</resultMap>

大卫,

我知道这有点老了,但我也在尝试实现一个 SQLite JDBC 模块,并且 运行 遇到完全相同的问题。我已经设法找到问题的根源,并在 JDBC SQLite github 页面上提交了一个问题:

https://github.com/xerial/sqlite-jdbc/issues/277

基本上,SQLite JDBC 驱动程序根据准备好的语句的参数计数计算其数组大小之一。但是,在您提到的情况下,有多个 SELECT 语句采用相同的参数,因此数组需要是 x * y (x = 参数计数,y = select 语句的数量)而不是不仅仅是参数的数量。当它尝试准备语句时,它命中超出参数计数的第一个位置并生成此异常。

其他 JDBC 模块 - MySQL、PostgreSQL 和 SQL 服务器,以及我正在处理的 Oracle 和 H2 - 似乎正确处理这种情况(好吧,Oracle 有点……特殊……),但 SQLite 驱动程序没有。

我能够以一种非常非常笨拙的方式解决这个问题,方法是创建两个不同的结果映射,一个用于通用 select,另一个用于检查 READ 权限的读取,然后将每个 select 查询分解为自己的 SELECT 块,并从结果映射中的集合中调用这些查询。它远没有现有代码那么优雅,但它确实有效。