You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:47:02