mybatis操作mariadb駆動mysql一括挿入エラーjava.nio.BufferOverflowException:null

6162 ワード

mybatis操作mariadb駆動mysql一括挿入エラーjava.nio.BufferOverflowException:null
ビジネスでは定期的にデータベースに統計データを書き込む必要があり、データ量が大きい.データベースへの書き込み中にバッファオーバーフロー異常が報告されることがあります.最後の例外スタックは次のとおりです.
Caused by: java.nio.BufferOverflowException: null at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189) at java.nio.ByteBuffer.put(ByteBuffer.java:859) at org.mariadb.jdbc.internal.packet.send.SendExecutePrepareStatementPacket.send(SendExecutePrepareStatementPacket.java:105) at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executePreparedQuery(AbstractQueryProtocol.java:578) at org.mariadb.jdbc.MariaDbServerPreparedStatement.executeInternal(MariaDbServerPreparedStatement.java:279) at org.mariadb.jdbc.MariaDbServerPreparedStatement.execute(MariaDbServerPreparedStatement.java:369) at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493) … 31 common frames omitted
苦痛なソースコードの解読を経て、ついに異常な発生原因を見つけた.例外スタックのS e n d ExecutePrepareStatementPacketクラスのsendメソッドから言えば、ソースコードは次のとおりです.
public int send(final OutputStream os) throws IOException {
        PacketOutputStream buffer = (PacketOutputStream) os;
        buffer.startPacket(0, true);
        buffer.buffer.put((byte) 0x17);
        buffer.buffer.putInt(statementId);
        buffer.buffer.put((byte) 0x00); //CURSOR TYPE NO CURSOR TODO implement when using cursor
        buffer.buffer.putInt(1); //Iteration count

        //create null bitmap
        if (parameterCount > 0) {
            int nullCount = (parameterCount + 7) / 8;
            byte[] nullBitsBuffer = new byte[nullCount];
            for (int i = 0; i < parameterCount; i++) {
                if (parameters[i] instanceof NullParameter) {
                    nullBitsBuffer[i / 8] |= (1 << (i % 8));
                }
            }
            buffer.buffer.put(nullBitsBuffer);/*Null Bit Map*/

            //check if parameters type (using setXXX) have change since previous request, and resend new header type if so
            boolean mustSendHeaderType = false;
            if (parameterTypeHeader[0] == null) {
                mustSendHeaderType = true;
            } else {
                for (int i = 0; i < this.parameterCount; i++) {
                    if (!parameterTypeHeader[i].equals(parameters[i].getMariaDbType())) {
                        mustSendHeaderType = true;
                        break;
                    }
                }
            }

            if (mustSendHeaderType) {
                buffer.buffer.put((byte) 0x01);
                //Store types of parameters in first in first package that is sent to the server.
                for (int i = 0; i < this.parameterCount; i++) {
                    parameterTypeHeader[i] = parameters[i].getMariaDbType();
                    parameters[i].writeBufferType(buffer);
                }
            } else {
                buffer.buffer.put((byte) 0x00);
            }
        }
        for (int i = 0; i < parameterCount; i++) {
            if (parameters[i] instanceof NotLongDataParameterHolder) {
                ((NotLongDataParameterHolder) parameters[i]).writeBinary(buffer);
            }
        }
        buffer.finishPacket();
        return 0;
    }

ソースコードのnullBitsBuffer配列のサイズは、入力されたパラメータのデータ量によって決まります.buffer.buffer.put(nullBitsBuffer)文のbuffer.bufferは、次のコードで構成されています.
public PacketOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
        buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
        this.seqNo = -1;
        useCompression = false;
    }

そのためnullBitsBufferサイズが1024を超えるとエラーが発生します.コミットするたびにデータベースのデータ量が大きすぎないことを確認する必要があります.