Room数据库开发:寻求可替代Cursor的Java类方案
替代Room数据库中Cursor类的方案推荐
我正在开发一款基于Room库的本地数据库简易应用,现需替换Cursor类,寻找功能等价的Java类,核心目标是利用数据库中的数据生成登记票据。以下是当前使用Cursor的PenaltyRegister类实现:
public class PenaltyRegister extends RegistroBase { /** * ATRIBUTOS DE LA CLASE */ private Cursor registerCursor = null; private ByteBuffer register; private short lengthData = 0; private short lengthPenalty = 329; private short lengthDataPlus = 23; protected int cardDataLength = 150; protected int freePenaltyDescriptionLength = 150; protected int documentLength = 20; private int dailySequenceStock = 112233; private String filenamePenalty = "PENALTY"; private char EquitmentType = 3; private char EquitmentModel = 3; private long serialNumbert = 3456789; private char line = 1; private int station = 2; private char hall = 3; private char position = 4; private int shiftNumber = 5; private long operationNumber = 8; private char supportType = 1; private char supportSubtype = 1; private byte[] cardData = new byte[cardDataLength]; private char penaltyReason = 167; private byte[] freePenaltyDescription = new byte[freePenaltyDescriptionLength]; private long penaltyAmount = 50; private char identification = 1; private char identificationType = 1; private int countryCode = 724; private byte[] document = new byte[documentLength]; /** * METODOS DE LA CLASE */ /** * @brief Constructor regitro Penalty //Code 89 */ public PenaltyRegister(int code, int version, int dailySeq, String fileName, Cursor p_register, Calendar register_Date, long offsetRegister){ super(code, version); this.registerCursor = p_register; Date registerDate = register_Date.getTime(); this.lengthData = (short) (getLengthRegisterBase() + this.lengthPenalty ); identification = (char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.IDENTIFICATION)); if(identification == 1) this.lengthData += lengthDataPlus; setLength(lengthData); setDate(registerDate.getTime()); setFechaRegistro(registerDate); setUniversalSequence(this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.UNIVERSAL_SEQ))); if(dailySeq == 0) dailySeq = dailySequenceStock; setDailySequence(dailySeq); setOffset(offsetRegister); setFileName(fileName); for (int st=0;st<cardDataLength; st++) cardData[st] = 0x01; this.create(); } /** * @brief Crea array de bytes del registro */ private final void create() { short numOfBytes = this.lengthData; register = ByteBuffer.allocate(numOfBytes); setProfundidad((char)numOfBytes); putUnsignedChar((char) getTipoRegistro()); putUnsignedChar((char) getVersionRegistro()); putUnsignedShort(numOfBytes); putUnsignedInt(getIndra4b(getFechaRegistro())); putUnsignedInt(universalSequence); putUnsignedInt(dailySequence); putUnsignedInt(offset); putArrayBytes(filename); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.EQUIPMENT_TYPE))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.EQUIPMENT_MODEL))); putUnsignedInt(this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.SERIAL_NUMBER))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.LINE))); putUnsignedShort(this.registerCursor.getShort(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.STATION))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.HALL))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.POSITION))); putUnsignedShort(this.registerCursor.getShort(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.SHIFT_NUMBER))); putUnsignedInt(this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.OPERATION_NUMBER))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.SUPPORT_TYPE))); putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.SUPPORT_SUBTYPE))); //CardData tiene que ser proporcionado por la CTL a través de parámetro del constructor putCardDataBytes(Base64.decode(this.registerCursor.getString(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.CARD_DATA)), Base64.DEFAULT)); int penalty_reason = this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.PENALTY_REASON)); putUnsignedChar((char)penalty_reason); if(penalty_reason == 0){ setDescription(this.registerCursor.getString(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.PENALTY_DESCRIPTION))); } putArrayBytes(freePenaltyDescription); putUnsignedInt(this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.PENALTY_AMOUNT))); putUnsignedChar(identification); if (identification == 1) { putUnsignedChar((char)this.registerCursor.getInt(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.IDENTIFICATION_TYPE))); putUnsignedShort(this.registerCursor.getShort(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.COUNTRY_CODE))); setDocument(this.registerCursor.getString(this.registerCursor.getColumnIndex(CoreDbContract.Penalty.DOCUMENT))); putArrayBytes(document); } putUnsignedInt(this.getCRC(this.register.array())); this.registerMaster = this.register; } /** * @brief introduce un long en el buffer del registro * @param value valor añadido */ private final void putUnsignedInt(long value){ this.register.put((byte) ((value & 0x000000FF) >> 0)); this.register.put((byte) ((value & 0x0000FF00) >> 8)); this.register.put((byte) ((value & 0x00FF0000) >> 16)); this.register.put((byte) ((value & 0xFF000000) >> 24)); } /** * @brief introduce un entero en el buffer del registro * * @param value valor añadido */ private final void putUnsignedShort(int value){ this.register.put((byte) ((value & 0x000000FF) >> 0)); this.register.put((byte) ((value & 0x0000FF00) >> 8)); } /** * @brief introduce un byte en el buffer del registro * @param value valor añadido */ private final void putUnsignedChar(char value){ this.register.put((byte)value); } /** * @brief introduce un array de bytes en el buffer del registro * @param value valor añadido */ private final void putArrayBytes(byte[] value){ for(int i=0;i<value.length;i++) this.register.put(value[i]); } /** * @brief introduce un array de bytes en el buffer del registro para el campo Card Data * (los dos primeros bytes son filtrados) * @param value valor añadido */ private final void putCardDataBytes(byte[] value) { for(int i=2; i<value.length; i++) { this.register.put(value[i]); } } /** * @brief Genera un array de bytes de los valores del registro * @return */ public ByteBuffer getBytesRegistro() { return register; } /** * @brief Devielve la longitud del registro en Bytes * @return Número de bytes que ocopa el registro */ public long getSizeRegistro() { return register.array().length; } /** * @brief Calculates and returns the Checksum of the register * @param bytes bytes del registro * @return Checksum del registro */ private long getChecksum(byte bytes[]) { long checksumValue = 0; Checksum checksum = new CRC32(); // update the current checksum with the specified array of bytes checksum.update(bytes, 0, bytes.length); // get the current checksum value checksumValue = checksum.getValue(); return checksumValue; } private static int getChecksum16(final byte[] buffer) { /* Note the change here */ int crc = 0x1D0F; for (int j = 0; j < buffer.length ; j++) { crc = ((crc >>> 8) | (crc << 8) )& 0xffff; crc ^= (buffer[j] & 0xff);//byte to int, trunc sign crc ^= ((crc & 0xff) >> 4); crc ^= (crc << 12) & 0xffff; crc ^= ((crc & 0xFF) << 5) & 0xffff; } crc &= 0xffff; return crc; } /** * @brief Set Penalty description * @param p_description nombre archivo del registro * @return Registro base */ public RegistroBase setDescription(final String p_description){ byte[] array = p_description.getBytes(); for (int i=0; i<p_description.length(); i++) this.freePenaltyDescription[i]=array[i]; return this; } /** * @brief Set Document * @param p_document nombre archivo del registro * @return Registro base */ public RegistroBase setDocument(final String p_document){ byte[] array = p_document.getBytes(); for (int i=0; i<p_document.length(); i++) this.document[i]=array[i]; return this; } }
推荐替代方案
1. Room实体类(Entity)
这是最贴合Room设计理念的首选方案,直接将数据库表映射为Java实体类,通过Room的Dao接口查询返回实体对象,完全替代Cursor的手动字段读取操作,类型安全且可读性高。
实现步骤:
- 定义对应数据库表的实体类:
@Entity(tableName = CoreDbContract.Penalty.TABLE_NAME) public class PenaltyEntity { @ColumnInfo(name = CoreDbContract.Penalty.IDENTIFICATION) private int identification; @ColumnInfo(name = CoreDbContract.Penalty.UNIVERSAL_SEQ) private int universalSeq; @ColumnInfo(name = CoreDbContract.Penalty.EQUIPMENT_TYPE) private int equipmentType; @ColumnInfo(name = CoreDbContract.Penalty.EQUIPMENT_MODEL) private int equipmentModel; @ColumnInfo(name = CoreDbContract.Penalty.SERIAL_NUMBER) private int serialNumber; // 其他字段与表列一一对应,省略... // Getter/Setter方法 public int getIdentification() { return identification; } public void setIdentification(int identification) { this.identification = identification; } public int getUniversalSeq() { return universalSeq; } public void setUniversalSeq(int universalSeq) { this.universalSeq = universalSeq; } // 其余字段的Getter/Setter省略... }
- 修改Dao接口,返回实体类而非Cursor:
@Dao public interface PenaltyDao { @Query("SELECT * FROM " + CoreDbContract.Penalty.TABLE_NAME + " WHERE ...") // 补充你的查询条件 PenaltyEntity getTargetPenalty(); }
- 重构
PenaltyRegister的构造器和create方法:
// 构造器改为接收PenaltyEntity public PenaltyRegister(int code, int version, int dailySeq, String fileName, PenaltyEntity penalty, Calendar register_Date, long offsetRegister){ super(code, version); Date registerDate = register_Date.getTime(); this.lengthData = (short) (getLengthRegisterBase() + this.lengthPenalty ); identification = (char) penalty.getIdentification(); if(identification == 1) this.lengthData += lengthDataPlus; setLength(lengthData); setDate(registerDate.getTime()); setFechaRegistro(registerDate); setUniversalSequence(penalty.getUniversalSeq()); if(dailySeq == 0) dailySeq = dailySequenceStock; setDailySequence(dailySeq); setOffset(offsetRegister); setFileName(fileName); for (int st=0;st<cardDataLength; st++) cardData[st] = 0x01; this.create(penalty); } // 修改create方法,用实体类Getter替代Cursor操作 private final void create(PenaltyEntity penalty) { short numOfBytes = this.lengthData; register = ByteBuffer.allocate(numOfBytes); setProfundidad((char)numOfBytes); putUnsignedChar((char) getTipoRegistro()); putUnsignedChar((char) getVersionRegistro()); putUnsignedShort(numOfBytes); putUnsignedInt(getIndra4b(getFechaRegistro())); putUnsignedInt(universalSequence); putUnsignedInt(dailySequence); putUnsignedInt(offset); putArrayBytes(filename); putUnsignedChar((char) penalty.getEquipmentType()); putUnsignedChar((char) penalty.getEquipmentModel()); putUnsignedInt(penalty.getSerialNumber()); // 其余字段依次替换为penalty.getXXX()调用 // ... putCardDataBytes(Base64.decode(penalty.getCardData(), Base64.DEFAULT)); int penalty_reason = penalty.getPenaltyReason(); putUnsignedChar((char)penalty_reason); if(penalty_reason == 0){ setDescription(penalty.getPenaltyDescription()); } putArrayBytes(freePenaltyDescription); putUnsignedInt(penalty.getPenaltyAmount()); putUnsignedChar(identification); if (identification == 1) { putUnsignedChar((char) penalty.getIdentificationType()); putUnsignedShort(penalty.getCountryCode()); setDocument(penalty.getDocument()); putArrayBytes(document); } putUnsignedInt(this.getCRC(this.register.array())); this.registerMaster = this.register; }
2. 自定义DTO(数据传输对象)
如果实体类包含业务无关字段,或者需要组合多表数据,可以自定义DTO类,将查询结果映射到DTO后传入PenaltyRegister。这种方式灵活性更高,可通过手动映射或MapStruct等工具简化转换逻辑。
3. List<Object[]>(不推荐)
若不想定义额外类,可让Dao返回List<Object[]>,数组元素对应表的列数据。但这种方式无类型校验,可读性差,仅适合临时快速修改场景,不推荐长期使用。
方案优势对比
- Room实体类:类型安全、代码易维护、符合Room最佳实践,优先选择。
- 自定义DTO:灵活适配业务需求,适合复杂数据组合场景。
- List<Object[]>:无需额外类定义,但维护成本高,易出错。
内容的提问来源于stack exchange,提问作者Manuel Lucas
相关产品推荐
相关产品推荐

