“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”
background
I was obsessed with a problem for four days last week, and finally realized the crux of the problem when I had dinner yesterday. This bug, almost makes me wonder whether MyBatis framework processing binary type data is a bug?
As it turns out, the framework is robust and easy to use. I made a stupid mistake.
Problem Description: The certificate file of an application system is stored in the database as BLOB. The decryption of the certificate file is defined as a native method and implemented by THE C language. It is said that this is to increase the difficulty of blasting. Indeed, the decompilation of c. so files is somewhat difficult.
Certificate upload function. After uploading an encrypted certificate, the JNI method is invoked for verification. After verification, call DAO method based on MyBatis BaseMapper implementation, insert or update, store binary stream of certificate file to database.
The weird thing is: after the certificate file is updated or inserted, the binary data of the file is read again, which is not the same as the content passed, resulting in the decryption failure of the JNI method. Repeated tests are the same.
Tracking process
The method of certificate verification is a JNI. The input parameter is a binary stream of encrypted certificate files, which returns a Map object of certificate information:
public native Map<String, String> check(byte[] data) throws Throwable;
Copy the code
The certificate file DAO class was originally defined as a subclass of BaseMapper, using the default method for all functionality:
@Mapper
public interface MyFileDao extends BaseMapper<MyFile> {
}
Copy the code
MyBatis’ default method for byte[] is not compatible with the binary data stream. We re-implement the class, add and change SQL statements, and specify the JDBC type as BLOB:
<insert id="insert" parameterType="XX.MyFile">
insert into XX(ID, FILE, MODIFY_TIME)
values (#{id,jdbcType=DECIMAL}, #{file,jdbcType=BLOB},#{modifyTime,jdbcType=DECIMAL})
</insert>
Copy the code
After some operation, the sound is still the same.
Then, write a separate insert and update file and store it properly again. Compare the certificate upload with the DAO call logic. Look at the two execution process SQL statements, are exactly the same, really can not find the problem wow.
issue
After repeated verification for a whole afternoon, the binary data stream inserted and queried by upload operation is always inconsistent. The focus of suspicion is always when the database is entered. Is the data lost by MyBatis framework?
At dinner, I suddenly realized that the JNI method was passed a binary array and the same byte[] object was stored in the library. Is the byte array changed by JNI? Verify it now, Eureka! It is!
JNI is written by a colleague in the C language group. I don’t know how to implement it, and I didn’t expect to modify the array inside the method. Finally, I guess that C implementation uses this byte[] object to store the decrypted content, so it directly prints the data found by the new String(byte[]) database, which is the plaintext information of the certificate.
Fixed: Data changes as it passes through JNI, so make a copy of the data before calling JNI and use the backup data when inserting or updating.
byte[] copyData = new byte[myFile.length];
System.arraycopy(myFile,0,copyData,0,myFile.length);
Copy the code
Extended analysis: Byte [] type in DAO
containsbyte[]
Type of the table entity that executes the defaultupdate
,insert
When, the type is byte.Custom insert functions specify BLOB types:
targetFile,jdbcType=BLOB
Copy the code
The type in executing SQL becomesByteArrayInputStream
:Either type has no effect on the eventual insertion of binary stream information into the database.
The revelation of
The bug I encountered was purely a result of forgetting the basic features of Java OOP. The object data was different before and after insertion, indicating that the object information was changed, and I always suspected that there was something wrong with the framework.
An object is a reference. Different places operate on the same address, so they operate on the same data.
Different scenarios: Data needs to be shared, and the object is OK. You don’t want to manipulate objects directly, so copy them.