Android 数据库总结

前言

Android的数据库是很方便的,但是很多的时候,Android程序员很少使用数据库,包括我在内,目前在我们的项目中还没用过数据库,但是不常用不代表不需要掌握,我们来大体总结一下

SQLite

给个链接:Android SQLite详解

再给个链接:SQLite常用语句

我自己基于用户、帖子、访客三个表写了一个Demo:数据库Demo

首先创建一个DBHelper类继承SQLiteOpenHelper类,重写onCreate()和onUpgrade()方法,这个类主要用来创建数据库和建表

public class DBHelper extends SQLiteOpenHelper {

    private static final int DB_VERSION = 1;
    public static final String DB_NAME = "pengboboer.db";

    public static final String TABLE_NAME_USER = "t_user";
    public static final String TABLE_NAME_POST = "t_post";
    public static final String TABLE_NAME_VISITOR = "t_visitor";

    public static final String[] USER_COLUMNS = new String[] {"id","name","age","country"};
    public static final String[] POST_COLUMNS = new String[] {"postId","title","visitCount","publishTime","publisherUid"};
    public static final String[] VISITOR_COLUMNS = new String[] {"id","postId","visitorUid", "visitTime"};


    public DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String sqlUser = "create table if not exists " + TABLE_NAME_USER
                + " (id integer primary key autoincrement, name text, age integer, country text)";

        String sqlPost = "create table if not exists " + TABLE_NAME_POST
                + " (postId integer primary key autoincrement, title text, visitCount integer, publishTime text, publisherUid integer, foreign key(publisherUid) references " + TABLE_NAME_USER + "(id))";

        String sqlVisitor = "create table if not exists " + TABLE_NAME_VISITOR
                + " (id integer primary key, postId integer, visitorUid integer, visitTime text)";
        sqLiteDatabase.execSQL(sqlUser);
        sqLiteDatabase.execSQL(sqlPost);
        sqLiteDatabase.execSQL(sqlVisitor);

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        String sqlUser = "DROP TABLE IF EXISTS" + TABLE_NAME_USER;
        String sqlPost = "DROP TABLE IF EXISTS" + TABLE_NAME_POST;
        String sqlVisitor = "DROP TABLE IF EXISTS" + TABLE_NAME_VISITOR;
        sqLiteDatabase.execSQL(sqlUser);
        sqLiteDatabase.execSQL(sqlPost);
        sqLiteDatabase.execSQL(sqlVisitor);
        onCreate(sqLiteDatabase);
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
        if(!db.isReadOnly()) {
            db.execSQL("PRAGMA foreign_keys=ON;");
        }

    }
}

然后我们再创建一个DaoManager类用来处理一些操作方法,先看一下它的抽象类:

public interface Dao {

    boolean insert(String tableName, ContentValues contentValues);

    boolean delete(String tableName, int id);

    boolean update(String tableName, int id, ContentValues contentValues);


    /** 默认初始化往数据库插入一些数据 */
    boolean initSomeData();
    /** 数据库中是否存在数据 */
    boolean isExistData();

    /** 获取用户表所有的信息 */
    List<User> getAllUserData();
    /** 获取帖子表所有的信息 */
    List<Post> getAllPostData();
    /** 获取访客表所有的信息 */
    List<Visitor> getVisitorData(int postId);

    /** 获取首页帖子列表所有的信息 */
    List<MainPostItem> getAllPostItem();
    /** 获取帖子访客列表所有的信息 */
    List<VisitorItem> getAllVisitorItem(int postId);
    /** 获取某一帖子的阅读量 */
    int getPostVisitCount(int postId);


}

增删改,需先调用getWritableDatabase():

1、然后调用对应的方法,传入参数

2、调用通用的execSQL(String sql),通过语句直接操作

// 核心就是try中的几行代码,记得在finally中关闭事务和db
public boolean insert(String tableName, ContentValues contentValues) {
        SQLiteDatabase db = null;

        try {
            db = dbHelper.getWritableDatabase();
            db.beginTransaction();
            db.insertOrThrow(tableName, null, contentValues);
            db.setTransactionSuccessful();
            return true;
        } catch (SQLiteConstraintException e) {
            e.printStackTrace();
            Log.e(TAG, "主键重复", e);
        } catch (Exception e) {
            Log.e(TAG, "出现异常", e);
        } finally {
            if (db != null) {
                db.endTransaction();
                db.close();
            }
        }

        return false;
    }


// 删和改类似,上面beginTransaction()下面那行改为
db.delete(tableName, "id = ?", new String[]{String.valueOf(id)});
db.update(tableName, contentValues, "id = ?", new String[]{String.valueOf(id)});



// 至于说直接用sql语句,那么举个增的例子,其它一样
db.execSQL("insert into " + DBHelper.TABLE_NAME_USER + " (id, name, age, country) values (11, '马云', 55, '中国')");

查,需先调用getReadableDatabase():

1、使用query:传入一些参数来查

2、使用rawQuery:直接用语句

// 直接查
cursor = db.query(DBHelper.TABLE_NAME_USER, DBHelper.USER_COLUMNS, null, null, null, null, null);

// 用语句查
cursor = db.rawQuery(sql,null);

我的Demo中有两个表连接查询语句比较抽象:

// 查询所有的帖子
// 查询帖子表并连用户表查询发帖人的信息
String sql = "select * from " + DBHelper.TABLE_NAME_POST + " post left join " + DBHelper.TABLE_NAME_USER + " user on post.publisherUid = user.id";

// 查询某一帖子的访客
// 从访客表中查询某一帖子的访客,并连用户表查询访客的信息
String sql = "select * from " + DBHelper.TABLE_NAME_VISITOR + " visitor left join " + DBHelper.TABLE_NAME_USER + " user on visitor.visitorUid = user.id where visitor.postId = " + postId;

线程问题

线程的问题我简单说一下,sqlite是线程安全的,线程安全意味着速度变慢,如果不需要线程安全,可以禁用互斥锁。

三种线程模型:

  • 单线程模型:互斥锁被禁用,同一时间只允许一个线程访问
  • 多线程模型:一个连接在同一个时间只允许一个线程访问
  • 串行模型:开启所有锁,随意访问

为了保证最高的并发性,推荐:

  • 使用多线程模式
  • 使用WAL模式
  • 单线程写,多线程读
  • 避免长时间事务
  • 缓存sqlite3_prepare编译结果
  • 多语句通过BEGIN和COMMIT做显示事务,减少多次的自动事务消耗

对于sqlite_busy

  • 有写操作时,其它写和读操作会被驳回
  • 开启事务后提交事务之前,其它写操作会被驳回
  • 有读操作时,写操作会被驳回,读操作之间可以并发执行

 

再说一下这篇文章:Concurrent database access - Dmytro Danylyk

  1. 如果在两个线程新建了两个数据库连接,并且这两个线程同时去写入,那么其中一项不会被写入,并且报错:数据库被锁定。
  2. 要在多线程中使用数据库,要确保使用同一个数据库连接,所有我们使用一个单例模式
  3. 即使是使用了单例,也同样会报一个新的错误:尝试重新打开一个已经关闭的对象。因为我们使用同一个数据库连接,可以线程1关闭了数据库,而线程2还在用它,所以会报错。
  4. 一些人建议不要关闭数据库,但是这样的话会引发内存泄漏。
  5. 正确的方法是:写一个open和close的方法,方法内部有一个计数器,这样来解决上面提到的问题。
  6. 如此一来就可以使用数据库了,并且它是线程安全的

 

总结

数据库简单的使用就是这些,我个人用SQLite也很少,一些常用语句在上面的链接我有给到,那个文章总结的还不错。

把那些语句掌握了,基本用起来没啥问题。

我写了个Demo,模拟帖子、用户、访客,有兴趣的,尤其Android新手可以看下:数据库Demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值