xUtils中的dbUtils中,在应用升级的时候修改表结构

我们在做数据缓存的时候经常用到数据库,数据库在进行存储的时候特别灵活也比较简单。虽然好用,但是存在一个问题,就是我们的应用在进行迭代的时候可能随着需求的变化,我们需要对表结构进行修改(一般是添加字段),这就需要我们在进行应用升级的时候,改变表结构,这样就面临一个问题,由于新表和旧表不一致,可能在存储时发生一些异常导致应用崩溃。大多数应用的解决办法是,在升级的时候,将之前所建的表删除然后重建,这样是不会崩溃,但是又出现了一个新问题,原来的数据都丢失了。最近我遇到的需求就是保存用户资料的添加了一些字段,但是在升级之后还想保存原来的用户资料。我在数据库缓存的时候用的是xUtils里的dbUtils,它是通过注解实体类来实现数据存储的,特别方便。下面我先简单介绍一下它的用法:

第一步,先创建数据库

创建数据库

  DaoConfig config = new DaoConfig(context);

  config.setDbName("xUtils-demo"); //数据库名

  config.setDbVersion(1);  //数据库版本号

  DbUtils db = DbUtils.create(config);//db还有其他的一些构造方法,比如含有更新表版本的监听器的 假如不设置监听器默认在升级的时候会将旧版本的表删掉

第二步,根据需求创建实体类,然后进行注解,注解方式如下图

xUtils中的dbUtils中,在应用升级的时候修改表结构_第1张图片

下面这是常用到的一些Annotation(注解)    //注解可不等同于注释,不要混为一谈

  @Check    check约束
  @Column   列名
  @Finder   一对多、多对一、多对多关系(见sample的Parent、Child中的使用)
  @Foreign  外键
  @Id       主键,当为int类型时,默认自增。 非自增时,需要设置id的值
  @NoAutoIncrement  不自增
  @NotNull  不为空
  @Table    表名
  @Transient  不写入数据库表结构
  @Unique   唯一约束

第三步,创建实体类对象进行保存。

UserInfo userInfo=new UserInfo();

userInfo.setXX();

...

db.save(userInfo);

就这样数据库中就将你的数据保存起来了,你可能会有疑问,过程中我也没看到创建表啊,其实在save的时候,它内部会首先创建表(表名可以通过@Table来自定义 默认是你当前实体类的包名+类名,只不过将连接符"."换成了"_",例如com_example_bean_UserInfo  )

db.createTableIfNotExist(UserInfo.class);

当然我们也可以自己先调用这个方法去创建表。其实看到上面在创建表的时候传的是Class对象,大家也可以猜到,dbUtils内部是用了反射的机制将实体类的属性保存成了表的相应字段。

我们在使用数据库的时候一般会进行一些增删改查,dbUtils都有相应的方法,用法也很简单,在这我就说几个注意点吧,在保存数据的时候,我们必须给实体类某个字段注解Id 作为主键,因为它内部存储是靠它来作为主键,并且是递增的。而且在更新的时候它内部可是靠这个Id来进行的。所以在更新某一条记录的时候不要直接用new出的实体类去直接调用update方法,应该是先通过findFirst方法获取实体类对象,设置属性值后再去update,否则,它会新插入一条记录而不是去更新。

dbUtils的update方法好像是只能更新相应字段的值,但是并不支持修改表结构。所以在修改表结构的时候需要我们自子去写sql语句。

下面我们赶紧步入正题了,如何在升级应用的时候修改表结构呢? 上面我们在创建数据库的时候也提过,它还有一个构造函数可以传一个更新监听,如下:

DbUtils dbUtils=DbUtils.create(this,"dbname",2, new DbUtils.DbUpgradeListener() {
    @Override
    public void onUpgrade(DbUtils dbUtils, int oldVersion, int newVersion) {
       if(newVersion!=oldVersion){
         //按需求进行更新 
       }
    }
});

下面是我写的一个方法,用于更新表结构的数据,保存原来的数据
public static void updateTable(DbUtils dbUtils, Class<?> tClass) {
    try {
        if (dbUtils.tableIsExist(tClass)) {
            String tableName = tClass.getName();
            tableName = tableName.replace(".", "_");
            String sql = "select * from " + tableName;
            //声名一个map用来保存原有表中的字段
           Map<String, String> filedMap = new HashMap<>();
            //执行自定义的sql语句
           Cursor cursor = dbUtils.execQuery(sql);
            int count = cursor.getColumnCount();
            for (int i = 0; i < count; i++) {
                filedMap.put(cursor.getColumnName(i), cursor.getColumnName(i));
            }
            //cursor在用完之后一定要close
           cursor.close();
            //下面用到了一些反射知识,下面是获取实体类的所有私有属性(即我们更改表结构后的所有字段名)
           Field[] fields = UserInfo.class.getDeclaredFields();

            for (int i = 0; i < fields.length; i++) {
                if (filedMap.containsKey(fields[i].getName())) {
                 //假如字段名已存在就进行下次循环
                 continue;
                } else {
                    //不存在,就放到map中,并且给表添加字段
                   filedMap.put(fields[i].getName(), fields[i].getName());
                    //根据属性的类型给表增加字段
                    if (fields[i].getType().toString().equals("class java.lang.String")) {

                        dbUtils.execNonQuery("alter table " + tableName + " add " + fields[i].getName() + " TEXT ");
                    } else if (fields[i].getType().equals("int") || fields[i].getType().equals("long") || fields[i].getType().equals("boolean")) {
                        dbUtils.execNonQuery("alter table " + tableName + " add " + fields[i].getName() + " INTEGER ");
                    }
                }
            }


        }
    } catch (DbException e) {
        e.printStackTrace();
    }


}

方法中暂时没有删除字段,假如有实际需求大家可以根据这个思路去实现。拜拜~






你可能感兴趣的:(xUtils中的dbUtils中,在应用升级的时候修改表结构)