数据存储(2)使用SQL数据库

Define a Schema and Contract

使用SQL数据库最重要的原则是Schema(架构),数组库如何组织,其实就是表结构。我们一般会用一个同伴类,Contract 类,

明确指定架构的布局以一个系统的和并且自我描述的方式。

一个Contract类是定义URIs,tables,columns这些常量的容器。Contract类允许在同一个包的不同类之间使用同一个常量。这样,

在一个地方修改,其他地方不需要修改。(问题是,这些常量必须在一个包里,所以说,数据相关的代码要放在同一个包里面?)

一个组织Contract类的好方式是放置全局数据库的定义在这个类的根层。然后为每一个表创建一个内部类,枚举他们的列。

sunshine的这个WeatherContract类,定义了全局的URI路径:

// The "Content authority" is a name for the entire content provider, similar to the
// relationship between a domain name and its website.  A convenient string to use for the
// content authority is the package name for the app, which is guaranteed to be unique on the
// device.
public static final String CONTENT_AUTHORITY = "com.loveqiqi.sy.mysunshine";

// Use CONTENT_AUTHORITY to create the base of all URI‘s which apps will use to contact
// the content provider.
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

// Possible paths (appended to base content URI for possible URI‘s)
// For instance, content://com.example.android.sunshine.app/weather/ is a valid path for
// looking at weather data. content://com.example.android.sunshine.app/givemeroot/ will fail,
// as the ContentProvider hasn‘t been given any information on what to do with "givemeroot".
// At least, let‘s hope not.  Don‘t be that dev, reader.  Don‘t be that dev.
public static final String PATH_WEATHER = "weather";
public static final String PATH_LOCATION = "location";

每个表有一个静态内部类:

这个内部类一般实现BaseColumns,这样就会有一个primary key, _ID.

这个类列举了表列的名词,然后提供了一些内容提供器需要的Uri。

/*
    Inner class that defines the table contents of the location table
    Students: This is where you will add the strings.  (Similar to what has been
    done for WeatherEntry)
 */
public static final class LocationEntry implements BaseColumns {
    public static final String TABLE_NAME = "location";

    public static final  String COLUMN_LOCATION_SETTING = "location_setting";
    public static final  String COLUMN_COORD_LAT = "coord_lat";
    public static final  String COLUMN_COORD_LONG = "coord_long";
    public static final  String COLUMN_CITY_NAME="city_name";

    public static final Uri CONTENT_URI =
            BASE_CONTENT_URI.buildUpon().appendPath(PATH_LOCATION).build();

    public static final String CONTENT_TYPE =
            ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION;
    public static final String CONTENT_ITEM_TYPE =
            ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION;

    public static Uri buildLocationUri(long id) {
        return ContentUris.withAppendedId(CONTENT_URI, id);
    }

}

Create a Database Using a SQL Helper

一旦我们定义了我们数据库的样子,应该实现方法,这些方法创建和维护数据库和表格。

这就要实现SQLiteOpenHelper了。

下面是例子:实现onCreate方法创建表。

/**
 * Manages a local database for weather data.
 */
public class WeatherDbHelper extends SQLiteOpenHelper {

    // If you change the database schema, you must increment the database version.
    private static final int DATABASE_VERSION = 2;

    static final String DATABASE_NAME = "weather.db";

    public WeatherDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        final String SQL_CREATE_WEATHER_TABLE = "CREATE TABLE " + WeatherEntry.TABLE_NAME + " (" +
                // Why AutoIncrement here, and not above?
                // Unique keys will be auto-generated in either case.  But for weather
                // forecasting, it‘s reasonable to assume the user will want information
                // for a certain date and all dates *following*, so the forecast data
                // should be sorted accordingly.
                WeatherEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +

                // the ID of the location entry associated with this weather data
                WeatherEntry.COLUMN_LOC_KEY + " INTEGER NOT NULL, " +
                WeatherEntry.COLUMN_DATE + " INTEGER NOT NULL, " +
                WeatherEntry.COLUMN_SHORT_DESC + " TEXT NOT NULL, " +
                WeatherEntry.COLUMN_WEATHER_ID + " INTEGER NOT NULL," +

                WeatherEntry.COLUMN_MIN_TEMP + " REAL NOT NULL, " +
                WeatherEntry.COLUMN_MAX_TEMP + " REAL NOT NULL, " +

                WeatherEntry.COLUMN_HUMIDITY + " REAL NOT NULL, " +
                WeatherEntry.COLUMN_PRESSURE + " REAL NOT NULL, " +
                WeatherEntry.COLUMN_WIND_SPEED + " REAL NOT NULL, " +
                WeatherEntry.COLUMN_DEGREES + " REAL NOT NULL, " +

                // Set up the location column as a foreign key to location table.
                " FOREIGN KEY (" + WeatherEntry.COLUMN_LOC_KEY + ") REFERENCES " +
                LocationEntry.TABLE_NAME + " (" + LocationEntry._ID + "), " +

                // To assure the application have just one weather entry per day
                // per location, it‘s created a UNIQUE constraint with REPLACE strategy
                " UNIQUE (" + WeatherEntry.COLUMN_DATE + ", " +
                WeatherEntry.COLUMN_LOC_KEY + ") ON CONFLICT REPLACE);";
        final  String SQL_CREATE_LOACTION_TABLE = "CREATE TABLE " + LocationEntry.TABLE_NAME + "(" +
                LocationEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                LocationEntry.COLUMN_LOCATION_SETTING + " TEXT UNIQUE NOT NULL," +
                LocationEntry.COLUMN_COORD_LAT + " REAL NOT NULL,"+
                LocationEntry.COLUMN_COORD_LONG + " REAL NOT NULL," +
                LocationEntry.COLUMN_CITY_NAME + " TEXT NOT NULL); ";

        sqLiteDatabase.execSQL(SQL_CREATE_WEATHER_TABLE);
        sqLiteDatabase.execSQL(SQL_CREATE_LOACTION_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        // Note that this only fires if you change the version number for your database.
        // It does NOT depend on the version number for your application.
        // If you want to update the schema without wiping data, commenting out the next 2 lines
        // should be your top priority before modifying this method.
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + LocationEntry.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + WeatherEntry.TABLE_NAME);
        onCreate(sqLiteDatabase);
    }
}

这样我们要访问数据库,使用这个类获得数据库的引用。系统执行潜在的长时间运行的创建更新数据库操作仅仅在需要的时候,不会再

应用启动。我们只要调用getWritableDatabase或者getReadableDatabase,因为可能长时间运行,所以要在后台进程调用,比如AsyncTask

或者IntentService(sunshine一直到SyncAdapter)。

public SQLiteDatabase getWritableDatabase
()说明

上面讨论系统在需要的时候去执行这些潜在的长时间运行的操作,在getWritableDatabase方法第一次调用的时候,数据库会打开,

然后onCreate(SQLiteDatabase),onUpgrade(SQLiteDatabase,
int, int)
and/oronOpen(SQLiteDatabase) will be
called.所以,

数据库的创建是在我们第一次调用这个函数的时候。数据库才被创建。

使用数据库的时候,直接这样:

然后sunshine的在ContentProvider的类的OnCreate中初始化定义的mOpenHelper。我们也只要这么使用就好。

mOpenHelper = new WeatherDbHelper(getContext());

然后就是对数据库的增删改查了。

数据库插入

使用ContentValues

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
         FeedEntry.TABLE_NAME,
         FeedEntry.COLUMN_NAME_NULLABLE,
         values);

第一个参数是表名,第二个参数是设置哪些列可以为空,如果设置为null,那么系统不会插入一个空行),一般是null吧,这样在定义

数据库的时候就需要知道哪些列是可为空的?如果要设置第二个参数的话.

数据库查询

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don‘t group the rows
    null,                                     // don‘t filter by row groups
    sortOrder                                 // The sort order
    );

public Cursor query (String table,String[]
columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

查询的第一个参数:表名

第二个:返回列,string数组,传递null,返回所有列,不推荐,阻止返回冗余数据

第三:哪些行返回,null返回所有行,会格式化为where语句

第四:如果在第三个参数包含?,这里需要传递? 的表示是数组

第五:Group by语句

第六:having 语句

第七:orderBy语句

上面,返回的值在一个Cursor中,如果要读数据,首先首先要调用cursor.moveToFirst从第一个开始,最好是加一个判断,以为如果

cursor为null,这个方法返回false。

This method will return false if the cursor is empty

然后就可以使用了:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

删除数据

删除数据要提供删除的行,像下面一样,防止SQL注入:

// Define ‘where‘ part of query.
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

public int delete(String table,String
whereClause, String[] whereArgs)

Returns

  • the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all rows and get a count pass "1" as the whereClause.

更新数据

下面是例子:

SQLiteDatabase db = mDbHelper.getWritableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);

时间: 2024-11-05 14:51:14

数据存储(2)使用SQL数据库的相关文章

定时从一个数据库表中的数据存储到另外一个数据库中的表,而且怎么处理重复的数据?

原文:http://www.iteye.com/problems/77856 定时从一个数据库表中的数据存储到另外一个数据库中的表,而且怎么处理重复的数据? 表结构肯定是不能破坏,但是临时表如果是自己的数据库还行,问题是这个Oracle数据库是客户的数据库呢,你不能在他的数据库做任何多余的操作吧?还有别的更好的方法吗? 这个真的是比较困难. 首先,你要从客户机oracle取数据,因为这1分钟间隔之内不知道用户机新增加了哪些数据(大部分情况下是用户使用别的系统插入数据,而你又没有这个系统的程序接口

Python3网络爬虫实战-33、数据存储:非关系型数据库存储:MongoDB

NoSQL,全称 Not Only SQL,意为不仅仅是 SQL,泛指非关系型的数据库.NoSQL 是基于键值对的,而且不需要经过 SQL 层的解析,数据之间没有耦合性,性能非常高. 非关系型数据库又可以细分如下: 键值存储数据库,代表有 Redis, Voldemort, Oracle BDB 等. 列存储数据库,代表有 Cassandra, HBase, Riak 等. 文档型数据库,代表有 CouchDB, MongoDB 等. 图形数据库,代表有 Neo4J, InfoGrid, Inf

Android本地数据存储之SQLite关系型数据库 ——SQLiteDatabase

数据库的创建,获取,执行sql语句: 框架搭建:dao 思考: 1.数据库保存在哪里? 2.如何创建数据库?如何创建表? 3.如何更新数据库?如何更改表的列数据? 4.如何获取数据库? 5.如何修改数据库中的表的数据? 框架思想 思考:如何使得编程更加简单? 一个sql语言,容易写错: 1.使用占位符: 2.框架解析重建法:搭建框架,对增删改查功能进行单独封装,传入容器对象即可: 思考: 1.数据库保存在哪里? data/data/包名/databases/xiaoo.db 2.如何创建数据库?

数据存储之非关系型数据库存储----MongoDB存储

MongoDB存储----文档型数据库 利用pymongo连接MongoDB import pymongo client = pymongo.MongoClient(host='localhost', port=27017) # 或 pymongo.MongoClient('mongodb://localhost:23017/') # 默认端口为:27017 # pymongo.MongoClient()方法 指定数据库 # 指定操作test数据库# db = client.test 或 db

大数据时代的数据存储,非关系型数据库MongoDB

在过去的很长一段时间中,关系型数据库(Relational Database Management System)一直是最主流的数据库解决方案,他运用真实世界中事物与关系来解释数据库中抽象的数据架构.然而,在信息技术爆炸式发展的今天,大数据已经成为了继云计算,物联网后新的技术革命,关系型数据库在处理大数据量时已经开始吃力,开发者只能通过不断地优化数据库来解决数据量的问题,但优化毕竟不是一个长期方案,所以人们提出了一种新的数据库解决方案来迎接大数据时代的到来——NoSQL(非关系型数据库). 为什

大数据时代的数据存储,非关系型数据库MongoDB(一)

爆炸式发展的NoSQL技术 在过去的很长一段时间中,关系型数据库(Relational Database Management System)一直是最主流的数据库解决方案,他运用真实世界中事物与关系来解释数据库中抽象的数据架构.然而,在信息技术爆炸式发展的今天,大数据已经成为了继云计算,物联网后新的技术革命,关系型数据库在处理大数据量时已经开始吃力,开发者只能通过不断地优化数据库来解决数据量的问题,但优化毕竟不是一个长期方案,所以人们提出了一种新的数据库解决方案来迎接大数据时代的到来——NoSQ

Python3网络爬虫实战-34、数据存储:非关系型数据库存储:Redis

Redis 是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单,在本节我们介绍一下 Python 的 Redis 操作,主要介绍 RedisPy 这个库的用法. 1. 准备工作 在本节开始之前请确保已经安装好了 Redis 及 RedisPy库,如果要做数据导入导出操作的话还需要安装 RedisDump,如没有安装可以参考第一章的安装说明. 2. Redis.StrictRedis RedisPy 库提供两个类 Redis 和 StrictRedi

看好你的门-保护数据存储区(1)-SQL注入防御

首先需要声明,本文纯属一个毫无远见和真才实学的小小开发人员的愚昧见解,仅供用于web系统安全方面的参考. 1.常用的SQL注入防御的方法 一个能连接数据库的应用. 1.对用户端输入的数据进行严格防范: 2.使用PreparedStatement执行Sql语句: 3.不仅仅要在页面层面进行验证,在服务端层面还要同步进行这些验证: 2.使用正则表达式屏蔽特殊字符 使用SQL注入攻击多在特殊字符上下手脚,如"'","*","/" ,"–&qu

【读书笔记《Android游戏编程之从零开始》】20.游戏开发基础(游戏数据存储)

对于数据的存储,Android 提供了4种保存方式. (1)SharedPreference 此方法适用于简单数据的保持,文如其名,属于配置性质的保存,不适合比较大的情况,默认存放在手机内存里 (2)FileInputStream/FileOutputStream 此方式比较适合游戏的保存和使用,流文件数据存储可以保持较大的数据,而且通过此方式不仅能把数据存储在手机内存中,也能将数据保存到手机额SDcard中. (3)SQLite 此方式也适合游戏的保存和使用,不仅可以保存较大的数据,而且可以将

四大组件之ContentProvider(三)-ContentProvider的数据存储

第4节 ContentProvider的数据存储 ContentProvider的数据存储经常使用SQL实现,当然也可以采用别的存储机制. 假设这个ContentProvider需要这样一个数据表: id name price 0 book0 15 1 book1 13 2 book2 18 4.1 SQL实现数据存储 4.1.1 创建数据库 Android SDK提供了SQLiteOpenHelper来方便开发者使用SQL数据库. 继承SQLiteOpenHelper,创建我们的帮助类, pu