SQLite数据库存储数据

来源:互联网 发布:微信淘客助手群发软件 编辑:程序博客网 时间:2024/05/29 19:56

这里写图片描述

简介:SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。许多开源项目((Mozilla, PHP, Python)都使用了 SQLite.SQLite 由以下几个组件组成:SQL 编译器、内核、后端以及附件。SQLite 通过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展 SQLite 的内核变得更加方便。

特点:

面向资源有限的设备,没有服务器进程,所有数据存放在同一文件中跨平台,可自由复制。

SQLite 内部结构:

这里写图片描述

SQLite 基本上符合 SQL-92 标准,和其他的主要 SQL 数据库没什么区别。它的优点就是高效,Android 运行时环境包含了完整的 SQLite。  SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite 称这为“弱类型”(manifest typing.)。 此外,SQLite 不支持一些标准的 SQL 功能,特别是外键约束(FOREIGN KEY constrains),嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 还有一些 ALTER TABLE 功能。 除了上述功能外,SQLite 是一个完整的 SQL 系统,拥有完整的触发器,交易等等。Android 集成了 SQLite 数据库 Android 在运行时(run-time)集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。 对于熟悉 SQL 的开发人员来时,在 Android 开发中使用 SQLite 相当简单。但是,由于 JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说并不合适。因此,Android 提供了一些新的 API 来使用 SQLite 数据库,Android 开发中,程序员需要学使用这些 API。数据库存储在 data/< 项目文件夹 >/databases/ 下。 Android 开发中使用 SQLite 数据库 Activites 可以通过 Content Provider 或者 Service 访问一个数据库。下面会详细讲解如果创建数据库,添加数据和查询数据库。 创建数据库 Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。

1.创建数据库

Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper 类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。SQLiteOpenHelper 的子类,至少需要实现三个方法:1 构造函数,调用父类 SQLiteOpenHelper 的构造函数。这个方法需要四个参数:上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null),一个代表你正在使用的数据库模型版本的整数。2 onCreate()方法,它需要一个 SQLiteDatabase 对象作为参数,根据需要对这个对象填充表和初始化数据。3 onUpgrage() 方法,它需要三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号,这样你就可以清楚如何把一个数据库从旧的模型转变到新的模型。下面示例代码展示了如何继承 SQLiteOpenHelper 创建数据库:
public class DatabaseHelper extends SQLiteOpenHelper {      DatabaseHelper(Context context, String name, CursorFactory cursorFactory, int version)   {         super(context, name, cursorFactory, version);          }          @Override         public void onCreate(SQLiteDatabase db) {              // TODO 创建数据库后,对数据库的操作          }          @Override     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {              // TODO 更改数据库版本的操作          }      @Override     public void onOpen(SQLiteDatabase db) {              super.onOpen(db);                // TODO 每次成功打开数据库后首先被执行          }      }     

2.数据的处理

SQLiteDatabase类为我们提供了很多种方法,上面的代码中基本上囊括了大部分的数据库操作;对于添加、更新和删除来说,我们都可以使用

1 db.executeSQL(String sql);
2 db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集

除了统一的形式之外,他们还有各自的操作方法:

1 db.insert(String table, String nullColumnHack, ContentValues values);
2 db.update(String table, Contentvalues values, String whereClause, String whereArgs);
3 db.delete(String table, String whereClause, String whereArgs);

以上三个方法的第一个参数都是表示要操作的表名;insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称,系统将此列设置为NULL,不至于出现错误;insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列名,value代表该列要插入的值;update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;delete方法的参数也是一样

下面给出demo

(1)数据的添加

1.使用insert方法

1 ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据2 cv.put("title","you are beautiful");//添加title3 cv.put("weather","sun"); //添加weather4 cv.put("context","xxxx"); //添加context5 String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")6                         .format(new Date());7 cv.put("publish ",publish); //添加publish8 db.insert("diary",null,cv);//执行插入操作

2.使用execSQL方式来实现

String sql = “insert into user(username,password) values (‘Jack Johnson’,’iLovePopMuisc’);//插入操作的SQL语句
db.execSQL(sql);//执行SQL语句

(2)数据的删除

同样有2种方式可以实现

String whereClause = “username=?”;//删除的条件
String[] whereArgs = {“Jack Johnson”};//删除的条件参数
db.delete(“user”,whereClause,whereArgs);//执行删除

使用execSQL方式的实现

String sql = “delete from user where username=’Jack Johnson’”;//删除操作的SQL语句
db.execSQL(sql);//执行删除操作

(3)数据修改

同上,仍是2种方式

ContentValues cv = new ContentValues();//实例化ContentValues
cv.put(“password”,”iHatePopMusic”);//添加要更改的字段及内容
String whereClause = “username=?”;//修改条件
String[] whereArgs = {“Jack Johnson”};//修改条件的参数
db.update(“user”,cv,whereClause,whereArgs);//执行修改

使用execSQL方式的实现

String sql = “update user set password = ‘iHatePopMusic’ where username=’Jack Johnson’”;//修改的SQL语句
db.execSQL(sql);//执行修改

(4)数据查询

下面来说说查询操作。查询操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询形式:

1 db.rawQuery(String sql, String[] selectionArgs);  2 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);  3 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);  4 db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);  

上面几种都是常用的查询方法,第一种最为简单,将所有的SQL语句都组织到一个字符串中,使用占位符代替实际参数,selectionArgs就是占位符实际参数集;

各参数说明:

table:表名称colums:表示要查询的列所有名称集selection:表示WHERE之后的条件语句,可以使用占位符selectionArgs:条件语句的参数数组groupBy:指定分组的列名having:指定分组条件,配合groupBy使用orderBy:y指定排序的列名limit:指定分页参数distinct:指定“true”或“false”表示要不要过滤重复值Cursor:返回值,相当于结果集ResultSet

最后,他们同时返回一个Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。下面是Cursor对象的常用方法:

1 c.move(int offset); //以当前位置为参考,移动到指定行   2 c.moveToFirst();    //移动到第一行   3 c.moveToLast();     //移动到最后一行   4 c.moveToPosition(int position); //移动到指定行   5 c.moveToPrevious(); //移动到前一行   6 c.moveToNext();     //移动到下一行   7 c.isFirst();        //是否指向第一条   8 c.isLast();     //是否指向最后一条   9 c.isBeforeFirst();  //是否指向第一条之前  10 c.isAfterLast();    //是否指向最后一条之后  11 c.isNull(int columnIndex);  //指定列是否为空(列基数为0)  12 c.isClosed();       //游标是否已关闭  13 c.getCount();       //总数据项数  14 c.getPosition();    //返回当前游标所指向的行数  15 c.getColumnIndex(String columnName);//返回某列名对应的列索引值  16 c.getString(int columnIndex);   //返回当前行指定列的值 

实现代码

String[] params =  {12345,123456};Cursor cursor = db.query("user",columns,"ID=?",params,null,null,null);//查询并获得游标if(cursor.moveToFirst()){//判断游标是否为空    for(int i=0;i<cursor.getCount();i++){        cursor.move(i);//移动到指定记录        String username = cursor.getString(cursor.getColumnIndex("username");        String password = cursor.getString(cursor.getColumnIndex("password"));    }}

通过rawQuery实现的带参数查询

Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");//Cursor c = db.rawQuery("s name, inventory FROM mytable where ID=?",new Stirng[]{"123456"});     result.moveToFirst(); while (!result.isAfterLast()) {     int id=result.getInt(0);     String name=result.getString(1);     int inventory=result.getInt(2);     // do something useful with these     result.moveToNext();  }  result.close();

在上面的代码示例中,已经用到了这几个常用方法中的一些,关于更多的信息,大家可以参考官方文档中的说明。

注意:最后当我们完成了对数据库的操作后,记得调用SQLiteDatabase的close()方法释放数据库连接,否则容易出现SQLiteException。

上面就是SQLite的基本应用,但在实际开发中,为了能够更好的管理和维护数据库,我们会封装一个继承自SQLiteOpenHelper类的数据库操作类,然后以这个类为基础,再封装我们的业务逻辑方法。

下面是一个参考案例:

1、首先创建数据库类

 1 import android.content.Context; 2 import android.database.sqlite.SQLiteDatabase; 3 import android.database.sqlite.SQLiteDatabase.CursorFactory; 4 import android.database.sqlite.SQLiteOpenHelper; 5  6 public class SqliteDBHelper extends SQLiteOpenHelper { 7  8     // 步骤1:设置常数参量 9     private static final String DATABASE_NAME = "diary_db";10     private static final int VERSION = 1;11     private static final String TABLE_NAME = "diary";12 13     // 步骤2:重载构造方法14     public SqliteDBHelper(Context context) {15         super(context, DATABASE_NAME, null, VERSION);16     }17 18     /*19      * 参数介绍:context 程序上下文环境 即:XXXActivity.this 20      * name 数据库名字 21      * factory 接收数据,一般情况为null22      * version 数据库版本号23      */24     public SqliteDBHelper(Context context, String name, CursorFactory factory,25             int version) {26         super(context, name, factory, version);27     }28     //数据库第一次被创建时,onCreate()会被调用29     @Override30     public void onCreate(SQLiteDatabase db) {31         // 步骤3:数据库表的创建32         String strSQL = "create table "33                 + TABLE_NAME34                 + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";35         //步骤4:使用参数db,创建对象36         db.execSQL(strSQL);37     }38     //数据库版本变化时,会调用onUpgrade()39     @Override40     public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {41 42     }43 }

正如上面所述,数据库第一次创建时onCreate方法会被调用,我们可以执行创建表的语句,当系统发现版本变化之后,会调用onUpgrade方法,我们可以执行修改表结构等语句。

2.Dao文件来封装我们所有的业务方法,代码如下:


1 import android.content.Context; 2 import android.database.Cursor; 3 import android.database.sqlite.SQLiteDatabase; 4  5 import com.chinasoft.dbhelper.SqliteDBHelper; 6  7 public class DiaryDao { 8  9     private SqliteDBHelper sqliteDBHelper;10     private SQLiteDatabase db;11 12     // 重写构造方法13     public DiaryDao(Context context) {14         this.sqliteDBHelper = new SqliteDBHelper(context);15         db = sqliteDBHelper.getWritableDatabase();16     }17 18     // 读操作19     public String execQuery(final String strSQL) {20         try {21             System.out.println("strSQL>" + strSQL);22             // Cursor相当于JDBC中的ResultSet23             Cursor cursor = db.rawQuery(strSQL, null);24             // 始终让cursor指向数据库表的第1行记录25             cursor.moveToFirst();26             // 定义一个StringBuffer的对象,用于动态拼接字符串27             StringBuffer sb = new StringBuffer();28             // 循环游标,如果不是最后一项记录29             while (!cursor.isAfterLast()) {30                 sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"31                         + cursor.getString(2) + "/" + cursor.getString(3) + "/"32                         + cursor.getString(4)+"#");33                 //cursor游标移动34                 cursor.moveToNext();35             }36             db.close();37             return sb.deleteCharAt(sb.length()-1).toString();38         } catch (RuntimeException e) {39             e.printStackTrace();40             return null;41         }42 43     }44 45     // 写操作46     public boolean execOther(final String strSQL) {47         db.beginTransaction();  //开始事务48         try {49             System.out.println("strSQL" + strSQL);50             db.execSQL(strSQL);51             db.setTransactionSuccessful();  //设置事务成功完成 52             db.close();53             return true;54         } catch (RuntimeException e) {55             e.printStackTrace();56             return false;57         }finally {  58             db.endTransaction();    //结束事务  59         }  60 61     }62 }

我们在Dao构造方法中实例化sqliteDBHelper并获取一个SQLiteDatabase对象,作为整个应用的数据库实例;在增删改信息时,我们采用了事务处理,确保数据完整性;最后要注意释放数据库资源db.close(),这一个步骤在我们整个应用关闭时执行,这个环节容易被忘记,所以朋友们要注意。

我们获取数据库实例时使用了getWritableDatabase()方法,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库实例呢?在这里我想和大家着重分析一下这一点。

我们来看一下SQLiteOpenHelper中的getReadableDatabase()方法:

 1 public synchronized SQLiteDatabase getReadableDatabase() {   2     if (mDatabase != null && mDatabase.isOpen()) {   3         // 如果发现mDatabase不为空并且已经打开则直接返回   4         return mDatabase;   5     }   6    7     if (mIsInitializing) {   8         // 如果正在初始化则抛出异常   9         throw new IllegalStateException("getReadableDatabase called recursively");  10     }  11   12     // 开始实例化数据库mDatabase  13   14     try {  15         // 注意这里是调用了getWritableDatabase()方法  16         return getWritableDatabase();  17     } catch (SQLiteException e) {  18         if (mName == null)  19             throw e; // Can't open a temp database read-only!  20         Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);  21     }  22   23     // 如果无法以可读写模式打开数据库 则以只读方式打开  24   25     SQLiteDatabase db = null;  26     try {  27         mIsInitializing = true;  28         String path = mContext.getDatabasePath(mName).getPath();// 获取数据库路径  29         // 以只读方式打开数据库  30         db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);  31         if (db.getVersion() != mNewVersion) {  32             throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to "  33                     + mNewVersion + ": " + path);  34         }  35   36         onOpen(db);  37         Log.w(TAG, "Opened " + mName + " in read-only mode");  38         mDatabase = db;// 为mDatabase指定新打开的数据库  39         return mDatabase;// 返回打开的数据库  40     } finally {  41         mIsInitializing = false;  42         if (db != null && db != mDatabase)  43             db.close();  44     }  45 }

在getReadableDatabase()方法中,首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。既然有可能调用到getWritableDatabase()方法,我们就要看一下了:

public synchronized SQLiteDatabase getWritableDatabase() {      if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {          // 如果mDatabase不为空已打开并且不是只读模式 则返回该实例          return mDatabase;      }      if (mIsInitializing) {          throw new IllegalStateException("getWritableDatabase called recursively");      }      // If we have a read-only database open, someone could be using it      // (though they shouldn't), which would cause a lock to be held on      // the file, and our attempts to open the database read-write would      // fail waiting for the file lock. To prevent that, we acquire the      // lock on the read-only database, which shuts out other users.      boolean success = false;      SQLiteDatabase db = null;      // 如果mDatabase不为空则加锁 阻止其他的操作      if (mDatabase != null)          mDatabase.lock();      try {          mIsInitializing = true;          if (mName == null) {              db = SQLiteDatabase.create(null);          } else {              // 打开或创建数据库              db = mContext.openOrCreateDatabase(mName, 0, mFactory);          }          // 获取数据库版本(如果刚创建的数据库,版本为0)          int version = db.getVersion();          // 比较版本(我们代码中的版本mNewVersion为1)          if (version != mNewVersion) {              db.beginTransaction();// 开始事务              try {                  if (version == 0) {                      // 执行我们的onCreate方法                      onCreate(db);                  } else {                      // 如果我们应用升级了mNewVersion为2,而原版本为1则执行onUpgrade方法                      onUpgrade(db, version, mNewVersion);                  }                  db.setVersion(mNewVersion);// 设置最新版本                  db.setTransactionSuccessful();// 设置事务成功              } finally {                  db.endTransaction();// 结束事务              }          }          onOpen(db);          success = true;          return db;// 返回可读写模式的数据库实例      } finally {          mIsInitializing = false;          if (success) {              // 打开成功              if (mDatabase != null) {                  // 如果mDatabase有值则先关闭                  try {                      mDatabase.close();                  } catch (Exception e) {                  }                  mDatabase.unlock();// 解锁              }              mDatabase = db;// 赋值给mDatabase          } else {              // 打开失败的情况:解锁、关闭              if (mDatabase != null)                  mDatabase.unlock();              if (db != null)                  db.close();          }      }  }

大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。

看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在DBManager构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取数据实例,如果遇到异常,再试图用getReadableDatabase()获取实例,当然这个时候你获取的实例只能读不能写了

3.数据库的使用,让我们看一下如何使用这些数据操作方法来显示数据,界面核心逻辑代码:


public class SQLiteActivity extends Activity {    public DiaryDao diaryDao;    //因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory);      //所以要确保context已初始化,我们可以把实例化Dao的步骤放在Activity的onCreate里    @Override    protected void onCreate(Bundle savedInstanceState) {        diaryDao = new DiaryDao(SQLiteActivity.this);        initDatabase();    }    class ViewOcl implements View.OnClickListener {        @Override        public void onClick(View v) {            String strSQL;            boolean flag;            String message;            switch (v.getId()) {            case R.id.btnAdd:                String title = txtTitle.getText().toString().trim();                String weather = txtWeather.getText().toString().trim();;                String context = txtContext.getText().toString().trim();;                String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")                        .format(new Date());                // 动态组件SQL语句                strSQL = "insert into diary values(null,'" + title + "','"                        + weather + "','" + context + "','" + publish + "')";                flag = diaryDao.execOther(strSQL);                //返回信息                message = flag?"添加成功":"添加失败";                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();                break;            case R.id.btnDelete:                strSQL = "delete from diary where tid = 1";                flag = diaryDao.execOther(strSQL);                //返回信息                message = flag?"删除成功":"删除失败";                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();                break;            case R.id.btnQuery:                strSQL = "select * from diary order by publish desc";                String data = diaryDao.execQuery(strSQL);                Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show();                break;            case R.id.btnUpdate:                strSQL = "update diary set title = '测试标题1-1' where tid = 1";                flag = diaryDao.execOther(strSQL);                //返回信息                message = flag?"更新成功":"更新失败";                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();                break;            }        }    }    private void initDatabase() {        // 创建数据库对象        SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this);        sqliteDBHelper.getWritableDatabase();        System.out.println("数据库创建成功");    }}

Android sqlite3数据库管理工具

Android SDK的tools目录下提供了一个sqlite3.exe工具,这是一个简单的sqlite数据库管理工具。开发者可以方便的使用其对sqlite数据库进行命令行的操作。

程序运行生成的.db文件一般位于”/data/data/项目名(包括所处包名)/databases/.db”,因此要对数据库文件进行操作需要先找到数据库文件:

1、进入shell 命令

adb shell

2、找到数据库文件

#cd data/data
#ls –列出所有项目
#cd project_name –进入所需项目名
#cd databases
#ls –列出现寸的数据库文件

3、进入数据库

#sqlite3 test_db –进入所需数据库

会出现类似如下字样:

SQLite version 3.6.22
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite>

至此,可对数据库进行sql操作。

4、sqlite常用命令

.databases –产看当前数据库
.tables –查看当前数据库中的表
.help –sqlite3帮助
.schema –各个表的生成语句

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝运营公司骗了怎么办 被金融公司骗了怎么办 天猫品牌方投诉怎么办 淘宝卖家售假被扣了12分怎么办? 淘宝店被投诉了怎么办 淘宝商品被投诉侵权怎么办 淘宝小二胡乱判怎么办 淘宝卖家不干了怎么办 花呗剩下的钱怎么办 蚂蚁花呗无法使用怎么办 淘宝不能用花呗怎么办 淘宝号给冻结了怎么办 淘宝买家号封了怎么办 拼多多商家盗图怎么办 被拼多多盗图了怎么办 淘宝盗用图片被投诉怎么办 淘宝别人盗用我的图片怎么办 淘宝盗图申诉原图过大怎么办 淘宝别人举报我盗用图片怎么办 淘宝卖家被投诉盗图怎么办 淘宝卖家被投诉卖假货怎么办 淘宝买到假货卖家不承认怎么办 被投诉盗图扣2分怎么办 拼多多盗淘宝图怎么办 微信视频清理了怎么办 牛仔短裤买大了怎么办 淘宝图片打开变大了怎么办 同城换公司社保怎么办 劳务不给交社保怎么办 好多工厂外包工不交社保怎么办 外包公司没有交社保怎么办 外包公司不给交社保怎么办 银行取100万现金怎么办 给老外发警告信后怎么办 照片上传是歪的怎么办 日亚不能直邮的怎么办 电话信息被卖了怎么办 被亚马逊跟卖了怎么办 玉米出芽后土壤不够湿怎么办 雨伞请输入授权码怎么办 网页放手机端后看不了怎么办