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

安卓健身追踪App的SQLite 1:M关系代码正确性验证求助

你的健身房训练追踪App SQLite DBHelper代码验证与优化建议

嘿,刚接触安卓和SQLite就能上手做训练追踪App,这很棒!我先结合你提到的Workouts与Exercises的1:M核心关系,给你梳理下DBHelper的验证要点和实用优化建议——假设你的代码已经具备基本结构,咱们一步步来:

1. 基础结构正确性检查

  • 首先确认SQLiteOpenHelper的继承与构造逻辑:你应该已经实现了带上下文、数据库名、游标工厂、版本号的构造方法,比如:
    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    
    游标工厂传null是安卓开发的标准做法,完全没问题;另外DATABASE_VERSION设为private static final int(比如初始值1)的做法你已经提到了,后续数据库升级时只需要递增这个数字即可,非常规范。

2. 核心关联关系(Workouts→Exercises 1:M)的实现验证

这是你App数据逻辑的核心,必须确保外键关联生效:

Workouts主表创建语句

你的主表应该包含自增主键作为关联依据,示例如下:

CREATE TABLE workouts (
    workout_id INTEGER PRIMARY KEY AUTOINCREMENT,
    workout_date TEXT NOT NULL,
    workout_name TEXT,
    notes TEXT
);
  • workout_id自增主键是关联Exercises表的核心,必须保证它的唯一性和自增特性。
  • 其他字段(日期、名称、备注)可以根据你的需求调整,NOT NULL约束可以强制必填项,避免无效数据。

Exercises从表创建语句

必须明确外键关联到Workouts表的workout_id,且要开启SQLite外键约束:

CREATE TABLE exercises (
    exercise_id INTEGER PRIMARY KEY AUTOINCREMENT,
    workout_id INTEGER NOT NULL,
    exercise_name TEXT NOT NULL,
    sets INTEGER NOT NULL,
    weight REAL NOT NULL,
    reps INTEGER NOT NULL,
    FOREIGN KEY (workout_id) REFERENCES workouts(workout_id) ON DELETE CASCADE
);

这里有两个关键细节:

  • 开启外键约束:SQLite默认禁用外键约束,所以你必须在onConfigure方法中手动开启,否则外键关联只是摆设:
    @Override
    public void onConfigure(SQLiteDatabase db) {
        super.onConfigure(db);
        db.setForeignKeyConstraintsEnabled(true);
    }
    
  • ON DELETE CASCADE设置:这个可选但非常实用,当删除某条Workout记录时,关联的所有Exercises记录会自动删除,避免遗留脏数据。

3. 增删改查(CRUD)方法的最佳实践

  • 插入数据:插入Workout后,要获取生成的workout_id再插入对应的Exercises,利用insert()方法的返回值(新插入记录的主键ID)即可:
    public long insertWorkout(Workout workout) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("workout_date", workout.getDate());
        values.put("workout_name", workout.getName());
        values.put("notes", workout.getNotes());
        long workoutId = db.insert("workouts", null, values);
        db.close();
        return workoutId;
    }
    
    拿到返回的workoutId后,就可以作为外键插入对应的Exercise数据了。
  • 查询关联数据:如果要获取某条Workout对应的所有Exercises,直接根据workout_id查询即可:
    public List<Exercise> getExercisesForWorkout(long workoutId) {
        List<Exercise> exercises = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.query("exercises",
                null,
                "workout_id = ?",
                new String[]{String.valueOf(workoutId)},
                null, null, null);
        if (cursor.moveToFirst()) {
            do {
                Exercise exercise = new Exercise();
                exercise.setId(cursor.getLong(cursor.getColumnIndex("exercise_id")));
                exercise.setWorkoutId(cursor.getLong(cursor.getColumnIndex("workout_id")));
                exercise.setName(cursor.getString(cursor.getColumnIndex("exercise_name")));
                exercise.setSets(cursor.getInt(cursor.getColumnIndex("sets")));
                exercise.setWeight(cursor.getDouble(cursor.getColumnIndex("weight")));
                exercise.setReps(cursor.getInt(cursor.getColumnIndex("reps")));
                exercises.add(exercise);
            } while (cursor.moveToNext());
        }
        cursor.close();
        db.close();
        return exercises;
    }
    
    注意一定要关闭Cursor和SQLiteDatabase,避免内存泄漏。
  • 更新与删除:更新Workout时无需手动同步Exercises;删除Workout时,利用ON DELETE CASCADE会自动删除关联的Exercises,减少手动操作的出错概率。

4. 其他细节优化

  • 常量定义:把表名、字段名定义为静态常量,避免硬编码拼写错误,比如:
    public static final String TABLE_WORKOUTS = "workouts";
    public static final String COLUMN_WORKOUT_ID = "workout_id";
    public static final String COLUMN_WORKOUT_DATE = "workout_date";
    // 其他字段同理
    
  • 数据库升级:后续修改表结构时,一定要在onUpgrade方法中做增量更新,不要直接删除表重建(会丢失用户数据),示例:
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 示例:从版本1升级到版本2,给workouts表增加训练时长字段
        if (oldVersion < 2) {
            db.execSQL("ALTER TABLE workouts ADD COLUMN duration INTEGER");
        }
    }
    
  • 异常处理:在CRUD方法中加入try-catch块捕获SQLiteException,避免App崩溃,同时可以记录日志方便排查问题。

内容的提问来源于stack exchange,提问作者user303749

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:06:38