第六章,数据持久化方案, 详解持久化技术
2018-03-28 15:57
651 查看
概述: 数据持久化,是指将内存中的瞬时数据保存到存储设备中, 当需要的时候可以恢复
Android数据持久化的三个方法 1.将简单的数据作为文件保存 2.SharedPreference 通过键值对来保存数据
3.通过数据库来存储数据
一,文件存储
Context类中提供 openFileOutput 和openFileInput 方法来读写数据 有两种模式可选 MODE_PRIVATE(默认操作模式会覆盖原文件的内容) MODE_APPEND
存储文件:
创建文本编辑框
?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.wangyamin.filepersistencetest.MainActivity">
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type something"/>
</LinearLayout>
修改Main_Activity中的代码 当活动销毁时保存文本框中已经输入的数据,打开应用时尝试加载保存的内容
二,SharedPreferences 通过键值对来保存数据
获取SharedPreferences 的三种方式
1.Context中的getSharedPreferences()方法 需要传入两个参数文件名和操作模式
2.Activity中的getPreferences() 只需要传入一个操作模式参数, 他会把当前的类名当做文件名
3.PreferenceManager类中的getDefaultSharedPreferences() 这是一个静态方法,接收Context参数,并自动使用当前程序的包名作为前缀来命名SharedPreferences文件
得到SharedPreferences 后操作数据的步骤
1.调用SharedPreferences .edit()方法 获取一个SharedPreferences .Editor对象
2.向SharedPreferences .Editor 对象添加数据 putString() ......
3.调用apply()方法来将添加的
ndroid:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context="com.example.wangyamin.sharedpreferencestest.MainActivity">
<Button
android:id="@+id/sava_data"
android:layout_width="match_parent"
android:text="sava data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/restore_data"
android:layout_width="match_parent"
android:text="restore_data"
android:layout_height="wrap_content" />
</LinearLayout>
三 SQLite 数据库存储
1.创建数据库
Android提供了一个SQLiteOpenHelper帮助类,这个类是抽象类 需要实现onCreate()和onUpgrade()方法
SQLiteOpenHelper中还有getReadableDatabase()和getWritableDatabase()方法,这两个方法都可以创建和打开一个现有的数据库并返回一个可以对这个数据库读写的对象
重写SQLiteOpenHelper的构造函数第一个参数是Context 2.数据库名称 3.查询返回的自定义Cursor一般传入null 4.版本号 public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book( id integer primary key autoincrement, author text, price real, pages integer,name text)";
public static final String CREATE_CATEGORY = "create table Category( id integer primary key autoincrement, category_name text, category_code integer)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL( CREATE_BOOK);
db.execSQL( CREATE_CATEGORY);
Toast.makeText( mContext,"Create Successed", Toast.LENGTH_LONG).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}布局文件中涉及了数据库的增删改查按钮<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.wangyamin.databasetest.MainActivity">
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:text="Add_data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/updata_data"
android:layout_width="match_parent"
android:text="Updata data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"/>
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"/>
</LinearLayout>Activity_Main 中是对逻辑的处理
//添加数据
db.execSQL("insert into Book(name, author, pages, price) values(? ,?,?,?)", new String[] { "The Dava", "Dan Brown", "454", "16.96"} );
//更新数据
db.execSQL("update Book set Price = ? where name = ?", new String[]{ "10.99", "The Dava"});
//删除数据
db.execSQL("delete from Book where pages > ?", new String[]{"500"});
//查询数据
db.rawQuery( "select * from Book", null );
四,使用开源库LitePal操作数据库
Litepal的使用步骤
1.配置Litepal, 在app/build.gradle 文件dependencies闭包中引入开源库compile'org.litepal.android:core:1.3.2' 2.配置litepal.xml文件 右击新建文件夹 app/src/main -new-Dirctory 创建一个assets目录 在assets目录下新建一个litepal.xml文件
?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value = "BookStore2"></dbname>
<version value ="3"></version>
<list><mapping class ="com.example.wangyamin.litepaltest.Book"/>
<mapping class="com.example.wangyamin.litepaltest.Category"/>
</list>
</litepal>dbname 指定数据库的名称
<list> 标签用于指定映射模型
mapping用于指定映射的类
3.配置LitePalApplication 注册 修改AndroidManifest.xml中的代码
android:name="org.litepal.LitePalApplication" 4.使用映射模型可以让我们用面向对象的思维来操作数据库 新建Book类 在litepal中添加映射关系
<list><mapping class ="com.example.wangyamin.litepaltest.Book"/>将这个类映射成表
调用Connector.getDatabase(); 方法就是一次最简单的数据库操作, 数据库即可创建完成
当数据库需要修改时只需要修改类就可以,然后将版本号加1即可,更新后litepal会帮我们保留之前表中的数据,不用担心更新后的数据丢失public class MainActivity extends AppCompatActivity {
private static final String TAG = "Book log";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button createDatabase = (Button)findViewById(R.id.create_db);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Connector.getDatabase();
}
});
//添加数据
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
book.save();
}
});
//修改数据
Button updataData = (Button)findViewById( R.id.updata_data );
updataData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
/* book.setName("The Lost Symbol");
book.setAuthor("Dan Brown");
book.setPages(510);
book.setPrice(19.96);
book.setPress("Unknow");
book.save();
book.setPrice(10.99);
book.save();*/
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?","The Lost Symbol", "Dan Brown");
}
});
//删除数据
Button deleteData = (Button)findViewById( R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataSupport.deleteAll( Book.class,"price < ?", "15" );
}
});
//查询数据 返回的是一个集合
Button queryData = (Button)findViewById( R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Book> books = DataSupport.findAll(Book.class);
for( Book book : books){
Log.d(TAG, "book name is "+book.getName());
Log.d(TAG, "book author is "+ book.getAuthor());
Log.d(TAG, "book price is "+ book.getPrice() );
}
}
});
}
}查询数据还可以使用
List<Book> books = DataSupport.select("name", "author", "pages").where( "pages>?", "400").order("pages").limit(10).offset(10).find(Book.class)
可以根据自己的需要组合查询语句
还可以使用DataSupport.findBySql()来进行原生查询 接收两个参数 第一个是sql语句 第二个是用于指定占位符的值
Android数据持久化的三个方法 1.将简单的数据作为文件保存 2.SharedPreference 通过键值对来保存数据
3.通过数据库来存储数据
一,文件存储
Context类中提供 openFileOutput 和openFileInput 方法来读写数据 有两种模式可选 MODE_PRIVATE(默认操作模式会覆盖原文件的内容) MODE_APPEND
存储文件:
创建文本编辑框
?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.wangyamin.filepersistencetest.MainActivity">
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type something"/>
</LinearLayout>
修改Main_Activity中的代码 当活动销毁时保存文本框中已经输入的数据,打开应用时尝试加载保存的内容
public class MainActivity extends AppCompatActivity { private EditText editText; // private String inputText = null; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText)findViewById(R.id.edit); String inputText = load(); Log.d(TAG, "gethahaset:"+load() ); if( !TextUtils.isEmpty(inputText) ){//TextUtils.isEmpty() 可以判断字符串的两种状态 null 或者 "" 都会返回true editText.setHint( inputText); } } @Override protected void onDestroy() { super.onDestroy(); String inputText = editText.getText().toString(); save( inputText); } private void save(String inputText) { FileOutputStream out = null; BufferedWriter writer = null; try { out = openFileOutput( "data", Context.MODE_PRIVATE); writer = new BufferedWriter( new OutputStreamWriter(out) ); writer.write( inputText ); }catch ( IOException e){ e.printStackTrace(); }finally { try { if( writer != null ){ writer.close(); } }catch ( IOException e){ e.printStackTrace(); } } } public String load(){ FileInputStream in = null; BufferedReader reader = null; StringBuilder sb = new StringBuilder(); try { in = openFileInput("data"); reader = new BufferedReader( new InputStreamReader(in) ); String line = ""; while ( (line = reader.readLine()) != null ){ sb.append( line ); } Log.d(TAG, "gethaha1:"+sb.toString() ); }catch ( IOException e){ e.printStackTrace(); }finally { try { if (in != null) { in.close(); } }catch (IOException e){ e.printStackTrace(); } } Log.d(TAG, "gethaha:"+sb.toString() ); return sb.toString(); } }读写完毕记得关闭文件流。
二,SharedPreferences 通过键值对来保存数据
获取SharedPreferences 的三种方式
1.Context中的getSharedPreferences()方法 需要传入两个参数文件名和操作模式
2.Activity中的getPreferences() 只需要传入一个操作模式参数, 他会把当前的类名当做文件名
3.PreferenceManager类中的getDefaultSharedPreferences() 这是一个静态方法,接收Context参数,并自动使用当前程序的包名作为前缀来命名SharedPreferences文件
得到SharedPreferences 后操作数据的步骤
1.调用SharedPreferences .edit()方法 获取一个SharedPreferences .Editor对象
2.向SharedPreferences .Editor 对象添加数据 putString() ......
3.调用apply()方法来将添加的
ndroid:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context="com.example.wangyamin.sharedpreferencestest.MainActivity">
<Button
android:id="@+id/sava_data"
android:layout_width="match_parent"
android:text="sava data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/restore_data"
android:layout_width="match_parent"
android:text="restore_data"
android:layout_height="wrap_content" />
</LinearLayout>
public class MainActivity extends AppCompatActivity { private static final String TAG = "TestSharedPreferences"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button saveData = (Button)findViewById(R.id.sava_data); saveData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SharedPreferences.Editor editor = getSharedPreferences( "data", MODE_PRIVATE).edit(); editor.putString( "name", "Tom"); editor.putInt("age", 28); editor.putBoolean("married", false ); editor.apply(); } }); Button restoreData = (Button)findViewById(R.id.restore_data); restoreData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SharedPreferences pref = getSharedPreferences( "data",MODE_PRIVATE); String name = pref.getString("name",""); int age = pref.getInt( "age",0); boolean married = pref.getBoolean("married",false); Log.d(TAG, "name is"+ name); Log.d(TAG, "age is"+age); Log.d(TAG, "married is "+married); } }); } }从SharedPreferences 中读取数据 SharedPreferences 对象中提供了一系列的get方法 传入两个参数,第一个是key 第二个是默认值
三 SQLite 数据库存储
1.创建数据库
Android提供了一个SQLiteOpenHelper帮助类,这个类是抽象类 需要实现onCreate()和onUpgrade()方法
SQLiteOpenHelper中还有getReadableDatabase()和getWritableDatabase()方法,这两个方法都可以创建和打开一个现有的数据库并返回一个可以对这个数据库读写的对象
重写SQLiteOpenHelper的构造函数第一个参数是Context 2.数据库名称 3.查询返回的自定义Cursor一般传入null 4.版本号 public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book( id integer primary key autoincrement, author text, price real, pages integer,name text)";
public static final String CREATE_CATEGORY = "create table Category( id integer primary key autoincrement, category_name text, category_code integer)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL( CREATE_BOOK);
db.execSQL( CREATE_CATEGORY);
Toast.makeText( mContext,"Create Successed", Toast.LENGTH_LONG).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}布局文件中涉及了数据库的增删改查按钮<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.wangyamin.databasetest.MainActivity">
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:text="Add_data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/updata_data"
android:layout_width="match_parent"
android:text="Updata data"
android:layout_height="wrap_content" />
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"/>
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"/>
</LinearLayout>Activity_Main 中是对逻辑的处理
public class MainActivity extends AppCompatActivity { private static final String TAG = "DB data"; private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstan c18f ceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper( this, "BookStore.db",null,5); Button createDatabase = (Button)findViewById(R.id.create_database); createDatabase.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dbHelper.getWritableDatabase(); } }); //向表中添加数据 Button addData = (Button)findViewById(R.id.add_data); addData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("name","The Da Vinci Code"); contentValues.put( "author", "Dan Brown"); contentValues.put("pages",454); contentValues.put("price", 16.96); db.insert("Book", null,contentValues); contentValues.clear(); // ContentValues contentValues = new ContentValues(); contentValues.put("name","The Lost Symbol"); contentValues.put( "author", "Dan Brown"); contentValues.put("pages",510); contentValues.put("price", 19.96); db.insert("Book", null,contentValues); } }); //更新表中的数据 Button updataData = (Button)findViewById(R.id.updata_data); updataData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("price", 10.99); db.update( "Book", contentValues, "name = ?", new String[]{ "The Da Vinci Code"}); } }); //删除表中的数据 Button deleteData = (Button)findViewById( R.id.delete_data); deleteData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book","pages>?", new String[]{"500"}); } }); //查询表中的数据 Button queryData = (Button)findViewById(R.id.query_data); queryData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); Cursor cursor = db.query( "Book", new String[]{"author"},null,null,null,null,null); if (cursor.moveToFirst()){ do{ // String name = cursor.getString( cursor.getColumnIndex("name")); String author = cursor.getString( cursor.getColumnIndex("author")); // int pages = cursor.getInt(cursor.getColumnIndex("pages")); // double price = cursor.getDouble( cursor.getColumnIndex("price")); // Log.d(TAG, "Book name is "+ name); Log.d(TAG, "Book author is "+ author); // Log.d(TAG, "Book pages is "+ pages); // Log.d(TAG, "Book price is "+price); }while ( cursor.moveToNext() ); } cursor.close(); } }); } }升级数据库一定要把版本号改大才会起作用,也可以使用原生sql查询语句
//添加数据
db.execSQL("insert into Book(name, author, pages, price) values(? ,?,?,?)", new String[] { "The Dava", "Dan Brown", "454", "16.96"} );
//更新数据
db.execSQL("update Book set Price = ? where name = ?", new String[]{ "10.99", "The Dava"});
//删除数据
db.execSQL("delete from Book where pages > ?", new String[]{"500"});
//查询数据
db.rawQuery( "select * from Book", null );
四,使用开源库LitePal操作数据库
Litepal的使用步骤
1.配置Litepal, 在app/build.gradle 文件dependencies闭包中引入开源库compile'org.litepal.android:core:1.3.2' 2.配置litepal.xml文件 右击新建文件夹 app/src/main -new-Dirctory 创建一个assets目录 在assets目录下新建一个litepal.xml文件
?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value = "BookStore2"></dbname>
<version value ="3"></version>
<list><mapping class ="com.example.wangyamin.litepaltest.Book"/>
<mapping class="com.example.wangyamin.litepaltest.Category"/>
</list>
</litepal>dbname 指定数据库的名称
<list> 标签用于指定映射模型
mapping用于指定映射的类
3.配置LitePalApplication 注册 修改AndroidManifest.xml中的代码
android:name="org.litepal.LitePalApplication" 4.使用映射模型可以让我们用面向对象的思维来操作数据库 新建Book类 在litepal中添加映射关系
<list><mapping class ="com.example.wangyamin.litepaltest.Book"/>将这个类映射成表
调用Connector.getDatabase(); 方法就是一次最简单的数据库操作, 数据库即可创建完成
当数据库需要修改时只需要修改类就可以,然后将版本号加1即可,更新后litepal会帮我们保留之前表中的数据,不用担心更新后的数据丢失public class MainActivity extends AppCompatActivity {
private static final String TAG = "Book log";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button createDatabase = (Button)findViewById(R.id.create_db);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Connector.getDatabase();
}
});
//添加数据
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
book.save();
}
});
//修改数据
Button updataData = (Button)findViewById( R.id.updata_data );
updataData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
/* book.setName("The Lost Symbol");
book.setAuthor("Dan Brown");
book.setPages(510);
book.setPrice(19.96);
book.setPress("Unknow");
book.save();
book.setPrice(10.99);
book.save();*/
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?","The Lost Symbol", "Dan Brown");
}
});
//删除数据
Button deleteData = (Button)findViewById( R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataSupport.deleteAll( Book.class,"price < ?", "15" );
}
});
//查询数据 返回的是一个集合
Button queryData = (Button)findViewById( R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Book> books = DataSupport.findAll(Book.class);
for( Book book : books){
Log.d(TAG, "book name is "+book.getName());
Log.d(TAG, "book author is "+ book.getAuthor());
Log.d(TAG, "book price is "+ book.getPrice() );
}
}
});
}
}查询数据还可以使用
List<Book> books = DataSupport.select("name", "author", "pages").where( "pages>?", "400").order("pages").limit(10).offset(10).find(Book.class)
可以根据自己的需要组合查询语句
还可以使用DataSupport.findBySql()来进行原生查询 接收两个参数 第一个是sql语句 第二个是用于指定占位符的值
相关文章推荐
- 第二行代码学习笔记——第六章:数据储存全方案——详解持久化技术
- 第六章 数据存储全方案,详解持久化技术
- 第六章 数据存储全方案-详解持久化技术
- 数据存储全方案,详解持久化技术
- 数据存储全方案,详解持久化技术
- 数据存储全方案,详解持久化技术
- 数据存储全方案--详解持久化技术
- day05 数据存储方案,详解持久化技术
- 【数据存储全方案,详解 持久化技术】实现记住密码功能
- 数据存储全方案,详解持久化技术
- android 摘要----数据存储全方案,详解持久化技术
- 阅读郭林《第一行代码》的笔记——第6章 数据存储全方案,详解持久化技术
- 数据存储全方案--详解持久化技术
- 【数据存储全方案,详解 持久化技术】SharedPreferences存储
- 第一行代码-第6章 数据存储方案,持久化技术
- 数据库存储全方案,详解持久化技术
- 第一行代码读书笔记——数据存储全方案,持久化技术
- iOS数据持久化方案详解
- Redis高可用详解:持久化技术及方案选择
- 数据存储方案:持久化技术