0
点赞
收藏
分享

微信扫一扫

R语言: mlr3机器学习--生存分析

代码展示

1.数据实体类
@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    private long id;
    private String name;
    private String age;
    private String sex;

    public User(String name, String age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

说明:用户表,包括id(作为主键,自动递增)、name、age、sex四个字段

2.Dao类
@Dao
public interface UserDao {

    @Update
    void updateUser(User user);

}

说明:只展示更新语句

3.用户仓库类
/**
 * 用户仓库类,负责用户数据的增删改查操作。
 */
public class UserRepository {

    private final UserDao userDao;

    /**
     * 构造函数,初始化用户数据访问对象。
     *
     * @param database 应用数据库实例。
     */
    public UserRepository(AppDatabase database) {
        this.userDao = database.getUserDao();
    }

    /**
     * 更新用户信息。如果用户不存在,则操作失败。
     *
     * @param user 要更新的用户对象。
     * @return 返回操作结果,成功或失败。
     */
    @Transaction
    public UserRepositoryResult updateUser(User user) {
        // 确认用户存在
        if (userDao.findUserByName(user.getName()) == null) {
            return new UserRepositoryResult(
                    UserRepositoryResult.Type.FAILURE,
                    RESULT_MESSAGE_USER_NOT_EXIST);
        }
        userDao.updateUser(user);
        return UserRepositoryResult.success();
    }

}

说明:只展示update实现

4.MainViewModel
/**
 * 主要的ViewModel类,用于处理与用户相关的数据操作。
 */
public class MainViewModel extends ViewModel {
    // 用户数据仓库接口
    private final UserRepository userRepository;
    // 执行器服务,用于在后台线程中执行数据库操作
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();

    /**
     * 构造函数,初始化用户数据仓库。
     *
     * @param userRepository 用户数据仓库实例。
     */
    public MainViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 用于存储更新用户操作结果的LiveData对象
    private final MutableLiveData<UserRepositoryResult> updateUserResult = new MutableLiveData<>();

    /**
     * 获取更新用户操作的结果。
     *
     * @return UserRepositoryResult 更新操作的结果。
     */
    public LiveData<UserRepositoryResult> getUpdateUserResult() {
        return updateUserResult;
    }

    // 更新用户
    public void updateUser(final User user) {
        EXECUTOR_SERVICE.execute(() -> {
            updateUserResult.postValue(userRepository.updateUser(user));
        });
    }

    /**
     * ViewModel工厂类,用于创建MainViewModel实例。
     */
    public static class Factory extends ViewModelProvider.NewInstanceFactory {
        // 用户数据仓库实例
        private final UserRepository userRepository;

        /**
         * 构造函数,初始化用户数据仓库。
         *
         * @param userRepository 用户数据仓库实例。
         */
        public Factory(UserRepository userRepository) {
            this.userRepository = userRepository;
        }

        /**
         * 创建MainViewModel实例。
         *
         * @param modelClass ViewModel的类类型。
         * @return MainViewModel 实例。
         */
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            return (T) new MainViewModel(userRepository);
        }
    }

}
5.MainActivity
/**
 * 主活动类,负责管理应用程序的主要界面。
 */
public class MainActivity extends AppCompatActivity {

    private MainViewModel viewModel; // 视图模型,用于管理活动背后的业务逻辑
    private ActivityMainBinding binding; // 数据绑定实例,用于简化UI更新
    private UserRepository userRepository;

    /**
     * 在活动创建时调用。
     *
     * @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 启用边缘到边缘的UI
        EdgeToEdge.enable(this);
        // 设置数据绑定
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        // 设置视图的内边距,以适应系统栏位的高度
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        userRepository = new UserRepository(AppDatabase.getDatabase(MVVMApplication.getInstance()));
        // 初始化视图模型
        viewModel = new ViewModelProvider(this, new MainViewModel.Factory(userRepository)).get(MainViewModel.class);
        binding.setViewModel(viewModel);
        initListeners();
        initObserver();
    }

    /**
     * 初始化视图监听器。
     */
    private void initListeners() {
        // 清空输入框操作监听
        binding.btnClearEdit.setOnClickListener(v -> {
            clearEditText();
        });

        // 更新用户操作监听
        binding.btnUpdate.setOnClickListener(v -> {
            Log.i("swy","update按钮被点击");
            User userChecked = checkUserInfo();
            if (userChecked != null) {
                Log.i("swy","更新用户");
                viewModel.updateUser(userChecked);
            }
        });

    }

    private void initObserver() {

        // 观察更新结果
        viewModel.getUpdateUserResult().observe(this, result -> {
            Log.i("swy","updateresult发生变化");
            if (result.getType() == UserRepositoryResult.Type.SUCCESS) {
                showToast("更新成功");
            } else {
                showToast(result.getErrorMessage());
            }
        });

    }

    // 封装对用户信息输入的验证
    private User checkUserInfo() {
        String name = binding.etName.getText().toString();
        if (name.isEmpty()) {
            showToast("请输入姓名");
            return null;
        }
        String age = binding.etAge.getText().toString();
        if (age.isEmpty()) {
            showToast("请输入年龄");
            return null;
        }
        String sex = binding.etSex.getText().toString();
        if (sex.isEmpty()) {
            showToast("请输入性别");
            return null;
        }
        return new User(name, age, sex);
    }

    // 封装对姓名输入的检查
    private String checkName() {
        String name = binding.etName.getText().toString();
        if (name.isEmpty()) {
            showToast("请输入姓名");
            return "";
        }
        return name;
    }

    // 清除编辑文本框中的内容
    private void clearEditText() {
        binding.etName.setText("");
        binding.etAge.setText("");
        binding.etSex.setText("");
    }

    // 简化Toast消息的显示
    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

}

至此,一套MVVM架构的Room数据库Update的语法即调用完成,感兴趣的同学可以多看一下这个流程和代码,观察一下是否有问题,多观察几分钟,分析一下流程,因为或许你正在跟我一样经历这个问题,或者未来可能被这类问题给绕进去。

好的,直接来说结果,上面的代码看似是完整的,且调用无误的。

但是里面有一个比较大的问题,这个问题在根儿上,也就是数据实体类,我们再次查看数据实体

@PrimaryKey(autoGenerate = true)
    private long id;
    private String name;
    private String age;
    private String sex;

前面也说了,这里面id为自增主键。

学过数据库的都知道,主键是用来区分两条数据的,比如说我这里有两条数据

id = 1,name = 张三,age = 18,sex = 男

id = 2,name = 张三,age = 18,sex = 男

假如有这么两条数据,id为主键的话,虽然名字年龄性别都是一样的张三,但是对于数据库而言,这是两条数据,对于用户表而言,这是两个同名的人

比如说,我给用户展现的是这样一个页面,支持常规的增删改查,但是用户只可以输入name、age、sex这三个数据

如果第一次用户输入了“name = 张三,age = 18,sex = 男”,点击增加按钮,显然是可以成功的

那么当用户再次输入“name = 张三,age = 18,sex = 男”,这里就有两种做法

第一种:允许输入

上面说了,user表以id为主键,所以从insert语句的执行来看,没有问题,就是完完全全的两条数据,这也是比较符合现实的,因为中国这么大,人这么多,肯定有很频繁的重名现象,所以一般我们来区分人并不是以姓名来区分,而是采用身份证号(ID),而后再因为身份证号码只有一个且比较重要不能随便示人,同时又要保证关联到人,我们现在一般用手机号来区分人,当然,手机号都有实名认证,即关联了身份证,且一个身份证可以办理多个手机号。当然,那个问题与本文无关,只是说到这里了。

第二种:不允许输入

这种设计方案一般在游戏里面比较常见,比如说,游戏刚开服,大家一窝蜂冲进去很重要一件事是干什么,抢注游戏昵称,因为如果某一个昵称比较好听或者寓意比较好,大家都想用,但是游戏的系统只允许一个人用,也就是不允许相同昵称出现这种策略。当然现在的大部分新游戏都是支持相同昵称的,所以在那些游戏的个人页面都支持一键赋值uid,这个uid就是游戏里面的身份证了,然后我们通过uid进行好友查找。

好了,上面描述了两种管理用户的策略,只是作为辅助思考

回归正题,讨论我前面展示的代码中的错误问题

通过上面的分析,我们知道了把id作为主键之后,其他属性并不足以区分用户这件关键信息了

所以来看这两部分的代码

1.从用户输入,组装更新User对象

// 封装对用户信息输入的验证
    private User checkUserInfo() {
        String name = binding.etName.getText().toString();
        if (name.isEmpty()) {
            showToast("请输入姓名");
            return null;
        }
        String age = binding.etAge.getText().toString();
        if (age.isEmpty()) {
            showToast("请输入年龄");
            return null;
        }
        String sex = binding.etSex.getText().toString();
        if (sex.isEmpty()) {
            showToast("请输入性别");
            return null;
        }
        return new User(name, age, sex);
    }

// 更新用户操作监听
        binding.btnUpdate.setOnClickListener(v -> {
            Log.i("swy","update按钮被点击");
            User userChecked = checkUserInfo();
            if (userChecked != null) {
                Log.i("swy","更新用户");
                viewModel.updateUser(userChecked);
            }
        });

2.传入user对象,执行Update操作

@Transaction
    public UserRepositoryResult updateUser(User user) {
        // 确认用户存在
        if (userDao.findUserByName(user.getName()) == null) {
            return new UserRepositoryResult(
                    UserRepositoryResult.Type.FAILURE,
                    RESULT_MESSAGE_USER_NOT_EXIST);
        }
        userDao.updateUser(user);
        return UserRepositoryResult.success();
    }

  @Update
    void updateUser(User user);

其实,说到这里,我相信大家已经看出来哪里出现问题了

我就不妨再说的细致一些吧,这是Room框架对于@Update注解的处理逻辑:

当您使用@Update注解进行用户数据更新时,Room库会根据传入的User对象的主键(即id)来定位到要更新的记录。具体处理逻辑如下:

a. 查找匹配的主键:Room会根据User对象的id值在数据库中查找具有相同id的记录。

b. 执行更新:如果找到了匹配的记录,则按照传入的User对象中的非空属性值更新对应的字段。这意味着如果您仅修改了name、age或sex中的某个属性值,并将这个更新后的User对象传递给@Update方法,只会更新这些已更改的属性,而不会影响其他未修改的属性或主键id。

c. 无匹配记录则不执行任何操作:如果数据库中找不到与传入User对象id相匹配的记录,Room将不做任何更新操作。 

 所以,问题就出现在了我们调用updateUser方法时,传入的user对象本身,即

new User(name, age, sex);

 可见,这里面是没有id的,那么没有id意味着什么,意味着id为null

对于Room而言,id为null时,它会怎么去理解我们传入的user对象呢——不做任何更新操作

所以,问题就找到了

也非常好解决,有两种解决方法,第一种就是给用户提供id的输入框(这种可以联想一下上面说过的游戏里面输入uid查找好友)

我们这里就简单一点吧

看一下我们的UserRepository中的具体调用

        // 确认用户存在
        if (userDao.findUserByName(user.getName()) == null) {
            return new UserRepositoryResult(
                    UserRepositoryResult.Type.FAILURE,
                    RESULT_MESSAGE_USER_NOT_EXIST);
        }

是的,你更新用户,首先得先确定用户存在对吧。

我是因为考虑一切从简,所以设计时是不允许相同name出现的,所以直接用name来查重了,当然也可以通过id来查找User

直接展示最终的处理办法

    @Transaction
    public UserRepositoryResult updateUser(User user) {
        // 确认用户存在
        User findUser = userDao.findUserByName(user.getName());
        if (findUser == null) {
            return new UserRepositoryResult(
                    UserRepositoryResult.Type.FAILURE,
                    RESULT_MESSAGE_USER_NOT_EXIST);
        }
        user.setId(findUser.getId());
        userDao.updateUser(user);
        return UserRepositoryResult.success();
    }

通过name找到数据库中的旧的User对象,因为id是主键,所以id不会更改,直接把旧的id塞到我们的新的user对象中,然后再传给updateUser方法,即可以实现Update方法了

举报

相关推荐

0 条评论