1

我正在尝试实现一个 Guice 模块,让Guacamole使用SQLite作为后端。Guacamole 项目有一个通用的 JDBC 基础模块。这使您可以用更少的代码为特定的数据存储实现模块。大多数代码行最终都位于映射器 XML 文件中。该项目提供PostgreSQL 和 MySQL 实现

我将这个 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

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

这是它有问题的查询:

<!-- 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 连接器的问题?

如果有帮助,您可以在此处查看该模块的代码。

这是与此查询相关的映射器参数的方法。ConnectionGroup 的完整映射器类在此处此处。我的 SQLite 模块的完整映射器 XML 在这里

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>
4

1 回答 1

1

大卫,

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

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

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

其他 JDBC 模块——MySQL、PostgreSQL 和 SQL Server,以及我正在搞砸的 Oracle 和 H2——似乎可以正确处理这种情况(嗯,Oracle 有点……特别……),但是SQLite 驱动程序没有。

通过创建两个不同的结果映射,一个用于通用选择,一个用于检查读取权限的读取,然后将每个选择查询分解为自己的 SELECT 块并从结果映射中的集合中调用它们。它远没有现有代码那么优雅,但它确实有效。

于 2017-10-13T16:18:05.277 回答