SQLiteOpenHelper类(SQLlite数据库详细解析--温故而知新)

1.简介

2.SQLiteOpenHelper类 常用方法

1. 创建 or 打开 可读/写的数据库(通过 返回的SQLiteDatabase对象 进行操作)
   getWritableDatabase()

 2. 创建 or 打开 可读的数据库(通过 返回的SQLiteDatabase对象 进行操作)
   getReadableDatabase()

 3. 数据库第1次创建时 则会调用,即 第1次调用 getWritableDatabase() / getReadableDatabase()时调用(在继承               SQLiteOpenHelper类的子类中复写)
   onCreate(SQLiteDatabase db) 

 4. 数据库升级时自动调用(在继承SQLiteOpenHelper类的子类中复写)
   onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

  5. 关闭数据库
   close()


 

3 . 数据库操作(增、删、减、查)

 

1. 查询数据
 (Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, 

   String orderBy, String limit)    查询指定的数据表返回一个带游标的数据集。

 参数说明: 
    table:表名称 
    colums:列名称数组 
    selection:条件子句,相当于where 
    selectionArgs:条件语句的参数数组 
    groupBy:分组 
    having:分组条件 
    orderBy:排序类 
    limit:分页查询的限制 
    Cursor:返回值,相当于结果集ResultSet 

 (Cursor) rawQuery(String sql, String[] selectionArgs) 运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的

    区别 = 防止SQL注入)

2. 删除数据行  
 (int) delete(String table,String whereClause,String[] whereArgs) 
 


 3. 添加数据行 
 (long) insert(String table,String nullColumnHack,ContentValues values) 
 
  4. 更新数据行 
(int) update(String table, ContentValues values, String whereClause, String[] whereArgs) 
 
 5. 执行一个SQL语句,可以是一个select or 其他sql语句  即 直接使用String类型传入sql语句 & 执行
 (void) execSQL(String sql) 

 

4.数据库的创建表时,字段的数据类型

  字段的数据类型:整数类型,浮点类型,日期类型,字符串类型,枚举类型与集合类型

1.   整数类型: int
 
2.   浮点类型:float double decimal

3.   日期类型  YEAR DATE TIME DATETIME TIMESTAMP

    作用:存储用户注册时间,文章发布时间等

    规则:
        YEAR
            YYYY(1901/2155)
        DATE
            YYYY-MM-DD(1000-01-01/9999-12-31)
        TIME
            HH:MM:SS('-838:59:59'/'838:59:59')
        DATETIME
            YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59    Y)
        TIMESTAMP
            YYYYMMDD HHMMSS(1970-01-01 00:00:00/2037 年某时)


    示例:create table t11(d date,t time,dt datetime);

            +-------+----------+------+-----+---------+-------+
            | Field | Type     | Null | Key | Default | Extra |
            +-------+----------+------+-----+---------+-------+
            | d     | date     | YES  |     | NULL    |       |
            | t     | time     | YES  |     | NULL    |       |
            | dt    | datetime | YES  |     | NULL    |       |
            +-------+----------+------+-----+---------+-------+


   datetime与timestamp的区别:

   DATETIME的日期范围是1001——9999年,TIMESTAMP的时间范围是1970——2038年。
   DATETIME使用8字节的存储空间,TIMESTAMP的存储空间为4字节。因此,TIMESTAMP空间利用率更高。
   DATETIME的默认值为null;TIMESTAMP的字段默认不为空(notnull),默认值为当前时间



 4.   字符串类型:char和varchar括号内的参数指的都是字符的长度


    1.char类型:定长,浪费空间,存取速度快

    字符长度范围:0-255(一个中文是一个字符,是utf8编码的3个字节)
    存储:
        存储char类型的值时,按指定宽度存,不够右填充空格,
        所以定长检索速度快,定长的宽度去解析。
        
        例如:指定长度为10,存>10个字符则报错,存<10个字符则用空格填充直到凑够10个字符存储

    检索:
        在检索或者说查询时,查出的结果显示时,会自动删除尾部的空格,除非我们打开 
        pad_char_to_full_length SQL模式(SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';)

    2.varchar类型:变长,精准,节省空间,存取速度慢,字符长度范围:0-65535(如果大于21845会提 
        示用其他类型 。mysql行最大限制为65535字节,字符编码为utf-8:
    存储:
        varchar类型存储数据的真实内容,不会用空格填充,如果'ab  ',尾部的空格也会被存起来,
        但是:varchar类型会在真实数据前加1-2Bytes的前缀,
        表示真实数据的bytes字节数,检索的时候才知道检索多少。
        如果真实的数据<255bytes则需要1Bytes的前缀(1Bytes=8bit 2**8最大表示的数字为255)
        如果真实的数据>255bytes则需要2Bytes的前缀(2Bytes=16bit 2**16最大表示的数字为65535)

    检索:
        尾部有空格会保存下来,在查询时,正常显示包含空格在内的内容

      #其他字符串系列(效率:char>varchar>text)

       TEXT系列 TINYTEXT TEXT MEDIUMTEXT LONGTEXT
       BLOB 系列    TINYBLOB BLOB MEDIUMBLOB LONGBLOB
       BINARY系列 BINARY VARBINARY

5.约束条件

1.primary key       标识该字段为该表的主键,可以唯一的标识记录


 主键,存储引擎innodb一张表必须有主键,不指定会自动检索不为空且唯一的字段创建,检索不到会创建一个隐藏字段。建表时指定主键。
 通常一张表,都应该有一个id字段用来标识记录编号,id通常设置为主键。
 一个表内只能有一个主键primary key
 =not null unique 不为空且唯一
 单列做主键:不为空且唯一;常用。
 多列做主键(复合主键):多个字段连在一起,当作主键;

create table department2(
      id int primary key, #主键
      name varchar(20),
      comment varchar(100)
     );




2.foreign key    标识该字段为该表的外键

 外键,建立表与表之间的关系。

 表类型必须是innodb存储引擎,且被关联的字段,即references指定的另外一个表的字段,必须保证唯一
 语法:foreign key(dep_id) references dep(id)
 最好不要用外键建立硬性关系,以后修改比较麻烦。

先建被关联的表,并且保证被关联的字段唯一
	create table dep(
		id int primary key,
		name char(16),
		comment char(50)
	);
	
#再建立关联的表
	create table emp(
		id int primary key,
		name char(10),
		sex enum('male','female'),
		dep_id int,
		foreign key(dep_id) references dep(id) 
		on delete cascade #如果没有,删除时候不能先删被关联的,也不能先更改被关联的。
		on update cascade # 被关联的有更新或者删除都会关联,没有逗号
	);

#先往被关联表插入记录
insert into dep values

#再往关联表插入记录
insert into emp values




3. not null          标识该字段不能为空

4. unique            标识该字段的值是唯一的

5. auto_increment    标识该字段的值自动增长(整数类型,而且为主键)

6. default           为该字段设置默认值

7. unsigend          无符号

8. zerofill          使用0填充

6. 自定义数据库实现类(继承 SQLiteOpenHelper 类)

 

SQLiteOpenHelper 类

public class MySQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String TAG = MySQLiteOpenHelper.class.getSimpleName();
    //数据库版本号
    private static Integer Version = 1;
    private  Context mContext;

    public MySQLiteOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {

        // 参数说明
        // context:上下文对象
        // name:数据库名称
        // param:一个可选的游标工厂(通常是 Null)
        // version:当前数据库的版本,值必须是整数并且是递增的状态

        // 必须通过super调用父类的构造函数
        super(context, name, factory, version);
        mContext=context;
    }
    public  MySQLiteOpenHelper( Context context,  String name,int Version){
        this(context, name, null,Version);
    }

    public  MySQLiteOpenHelper( Context context,  String name){
        this(context, name, Version);
    }

    /**
     * 复写onCreate()
     * 调用时刻:当数据库第1次创建时调用
     * 作用:创建数据库 表 & 初始化数据
     * SQLite数据库创建支持的数据类型: 整型数据、字符串类型、日期类型、二进制
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        //拼接sql语句
        //create table user 创建user表
        //id int primary key 创建一个id 字段数据类型为整形,且id唯一
        //name varchar(200)   创建一个name字段 数据类型为字符串类型,长度为200
        //age integer   创建一个age字段 数据类型为整数类型
        //path text   创建一个path字段 数据类型为字符串类型
        String sql ="create table user(id int primary key ,name varchar(200),age integer,path text )";
        //execSQL用于执行SQL语句,完成数据库的创建
        db.execSQL(sql);

    }

    /***
     * 数据库升级时调用
     * 如果VERSION值被改为2,系统发现现有数据库版本不同,即会调用onUpgrade()方法
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        Log.e(TAG,"更新数据库版本为:"+newVersion);

    }
}

 

7.创建数据库

 

 // 步骤1:创建DatabaseHelper对象
 // 注:此时还未创建数据库
   MySQLiteOpenHelper dbHelper1 = new MySQLiteOpenHelper(this,"test_db",1);         

// 步骤2:真正创建 / 打开数据库
 SQLiteDatabase sqliteDatabase = dbHelper.getWritableDatabase(); // 创建 or 打开 可读/写的数据库
 SQLiteDatabase sqliteDatabase = dbHelper.getReadableDatabase(); // 创建 or 打开 可读的数据库


对于操作 = “增、删、改(更新)”,需获得 可"读 / 写"的权限:getWritableDatabase()
对于操作 = “查询”,需获得 可"读 "的权限getReadableDatabase()

8 操作数据库(增、删、查、改)

 

 //点击创建数据库库
            case R.id.instablish:

                // 创建SQLiteOpenHelper子类对象
                MySQLiteOpenHelper text_db = new MySQLiteOpenHelper(this, "text_db");
                //数据库实际上是没有被创建或者打开的,直到getWritableDatabase() 或者 getReadableDatabase() 方法中的一个被调用时才会进行创建或者打开
                SQLiteDatabase writableDatabase = text_db.getWritableDatabase();
                MySQLiteOpenHelper dbHelper = new MySQLiteOpenHelper(this,"test_db");

                break;

            //点击更新数据
            case R.id.upgrade:
                //将版本号变大且是整数形式
                MySQLiteOpenHelper dbHelper_upgrade = new MySQLiteOpenHelper(this,"text_db",2);

                break;

            //点击插入数据到数据库
            case R.id.insert:
                Toast.makeText(this,"插入数据库",Toast.LENGTH_SHORT).show();
                // 创建SQLiteOpenHelper子类对象
                MySQLiteOpenHelper dbHelper1 = new MySQLiteOpenHelper(this,"test_db",1);
                SQLiteDatabase  sqliteDatabase1 = dbHelper1.getWritableDatabase();
                for (int i = 0; i < 10; i++) {
                    // 创建ContentValues对象
                    ContentValues contentValues = new ContentValues();
                    //其中,key = 列名,value = 插入的值
                    contentValues.put("name","张三"+i);
                    contentValues.put("age",i);
                    contentValues.put("id",i);
                    contentValues.put("path","www.baidu.com");

                    // 调用insert()方法将数据插入到数据库当中
                    // 参数1:要操作的表名称
                    // 参数2:SQl不允许一个空列,若ContentValues是空,那么这一列被明确的指明空字符串
                    // 参数3:ContentValues对象
                    sqliteDatabase1.insert("user","",contentValues);
                }

                //关闭数据库
                sqliteDatabase1.close();
                break;
            //点击查询数据库中所有数据
            case R.id.query:
                Toast.makeText(this,"查询数据库",Toast.LENGTH_SHORT).show();
                // 创建DatabaseHelper对象
                MySQLiteOpenHelper dbHelper4 = new MySQLiteOpenHelper(MainActivity.this,"test_db",1);
                SQLiteDatabase sqliteDatabase4 = dbHelper4.getReadableDatabase();
                // 调用SQLiteDatabase对象的query方法进行查询
                // 返回一个Cursor对象:由数据库查询返回的结果集对象

               // query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,
               //     String orderBy)
                // 参数说明
                // table:要操作的表
                // columns:查询的列所有名称集
                // selection:WHERE之后的条件语句,可以使用占位符
                // groupBy:指定分组的列名
                // having指定分组条件,配合groupBy使用
                // orderBy指定排序的列名
                // limit指定分页参数
                // distinct可以指定“true”或“false”表示要不要过滤重复值

                // 所有方法将返回一个Cursor对象,代表数据集的游标

                Cursor cursor = sqliteDatabase4.query("user", new String[] {
                        "name" ,"age","path","id"}, null/*"id=?"*/,null /*new String[] { "1" }*/, null, null, null);
                String name = null;
                int age ;
                int id ;
                String path = null;
                //将光标移动到下一行,从而判断该结果集是否还有下一条数据
                //如果有则返回true,没有则返回false
              /*  //Cursor对象常用方法如下:
                c.move(int offset); //以当前位置为参考,移动到指定行
                c.moveToFirst();    //移动到第一行
                c.moveToLast();     //移动到最后一行
                c.moveToPosition(int position); //移动到指定行
                c.moveToPrevious(); //移动到前一行
                c.moveToNext();     //移动到下一行
                c.isFirst();        //是否指向第一条
                c.isLast();     //是否指向最后一条
                c.isBeforeFirst();  //是否指向第一条之前
                c.isAfterLast();    //是否指向最后一条之后
                c.isNull(int columnIndex);  //指定列是否为空(列基数为0)
                c.isClosed();       //游标是否已关闭
                c.getCount();       //总数据项数
                c.getPosition();    //返回当前游标所指向的行数
                c.getColumnIndex(String columnName);//返回某列名对应的列索引值
                c.getString(int columnIndex);   //返回当前行指定列的值 */

                while (cursor.moveToNext()) {
                     id = cursor.getInt(cursor.getColumnIndex("id"));
                     age = cursor.getInt(cursor.getColumnIndex("age"));
                     name = cursor.getString(cursor.getColumnIndex("name"));
                      path = cursor.getString(cursor.getColumnIndex("path"));
                    //输出查询结果
                   Log.e("MainActivity","查询到的数据是:  "+"name: "+name+"age: "+age+"  "+"path: "+path);

                }
                //关闭数据库
                sqliteDatabase4.close();

                break;


            //点击修改数据
            case R.id.modify:
                Toast.makeText(this,"修改数据",Toast.LENGTH_SHORT).show();
                // 创建一个DatabaseHelper对象
                // 将数据库的版本升级为2
                // 传入版本号为2,大于旧版本(1),所以会调用onUpgrade()升级数据库
                MySQLiteOpenHelper dbHelper2 = new MySQLiteOpenHelper(MainActivity.this,"test_db", 1);
                // 调用getWritableDatabase()得到一个可写的SQLiteDatabase对象
                SQLiteDatabase sqliteDatabase2 = dbHelper2.getWritableDatabase();

                // 创建一个ContentValues对象
                ContentValues values2 = new ContentValues();
                values2.put("name", "王五");

                // 调用update方法修改数据库  //更新条件 修改表user 中 id=1 这一行中的name数据修改成zhangsan
                sqliteDatabase2.update("user", values2, "id=?", new String[]{"1"});

                //关闭数据库
                sqliteDatabase2.close();
                break;

            //点击删除数据
            case R.id.delete:
                Toast.makeText(this,"删除数据",Toast.LENGTH_SHORT).show();
                // 创建DatabaseHelper对象
                MySQLiteOpenHelper dbHelper3 = new MySQLiteOpenHelper(MainActivity.this,"test_db",1);

                // 调用getWritableDatabase()方法创建或打开一个可以读的数据库
                SQLiteDatabase sqliteDatabase3 = dbHelper3.getWritableDatabase();

                //删除数据
                sqliteDatabase3.delete("user", "id=?", new String[]{"1"});
                //关闭数据库
                sqliteDatabase3.close();
                break;




            //点击删除数据库
            case R.id.delete_database:
                Toast.makeText(this,"删除数据库",Toast.LENGTH_SHORT).show();
                //删除名为test.db数据库
                deleteDatabase("test_db");
                break;

            default:
                break;

 

9实例代码如下:

MainActivity 类  :按顺序点击:创建 - 插入 - 查询 - 修改 - 查询 - 删除数据- 更新

/***
 * SQLiteOpenHelper的使用
 *
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button instablish;
    private Button insert;
    private Button upgrade;
    private Button modify;
    private Button delete;
    private Button query;
    private Button delete_database;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        instablish = (Button) findViewById(R.id.instablish);
        insert = (Button) findViewById(R.id.insert);
        upgrade = (Button) findViewById(R.id.upgrade);
        modify = (Button) findViewById(R.id.modify);
        delete = (Button) findViewById(R.id.delete);
        query = (Button) findViewById(R.id.query);
        delete_database = (Button) findViewById(R.id.delete_database);

        //设置监听器
        instablish.setOnClickListener(this);
        insert.setOnClickListener(this);
        upgrade.setOnClickListener(this);
        modify.setOnClickListener(this);
        delete.setOnClickListener(this);
        query.setOnClickListener(this);
        delete_database.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {


            //点击创建数据库库
            case R.id.instablish:

                // 创建SQLiteOpenHelper子类对象
                MySQLiteOpenHelper text_db = new MySQLiteOpenHelper(this, "text_db");
                //数据库实际上是没有被创建或者打开的,直到getWritableDatabase() 或者 getReadableDatabase() 方法中的一个被调用时才会进行创建或者打开
                SQLiteDatabase writableDatabase = text_db.getWritableDatabase();
                MySQLiteOpenHelper dbHelper = new MySQLiteOpenHelper(this,"test_db");

                break;

            //点击更新数据
            case R.id.upgrade:
                //将版本号变大且是整数形式
                MySQLiteOpenHelper dbHelper_upgrade = new MySQLiteOpenHelper(this,"text_db",2);

                break;

            //点击插入数据到数据库
            case R.id.insert:
                Toast.makeText(this,"插入数据库",Toast.LENGTH_SHORT).show();
                // 创建SQLiteOpenHelper子类对象
                MySQLiteOpenHelper dbHelper1 = new MySQLiteOpenHelper(this,"test_db",1);
                SQLiteDatabase  sqliteDatabase1 = dbHelper1.getWritableDatabase();
                for (int i = 0; i < 10; i++) {
                    // 创建ContentValues对象
                    ContentValues contentValues = new ContentValues();
                    //其中,key = 列名,value = 插入的值
                    contentValues.put("name","张三"+i);
                    contentValues.put("age",i);
                    contentValues.put("id",i);
                    contentValues.put("path","www.baidu.com");

                    // 调用insert()方法将数据插入到数据库当中
                    // 参数1:要操作的表名称
                    // 参数2:SQl不允许一个空列,若ContentValues是空,那么这一列被明确的指明空字符串
                    // 参数3:ContentValues对象
                    sqliteDatabase1.insert("user","",contentValues);
                }

                //关闭数据库
                sqliteDatabase1.close();
                break;
            //点击查询数据库中所有数据
            case R.id.query:
                Toast.makeText(this,"查询数据库",Toast.LENGTH_SHORT).show();
                // 创建DatabaseHelper对象
                MySQLiteOpenHelper dbHelper4 = new MySQLiteOpenHelper(MainActivity.this,"test_db",1);
                SQLiteDatabase sqliteDatabase4 = dbHelper4.getReadableDatabase();
                // 调用SQLiteDatabase对象的query方法进行查询
                // 返回一个Cursor对象:由数据库查询返回的结果集对象

               // query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,
               //     String orderBy)
                // 参数说明
                // table:要操作的表
                // columns:查询的列所有名称集
                // selection:WHERE之后的条件语句,可以使用占位符
                // groupBy:指定分组的列名
                // having指定分组条件,配合groupBy使用
                // orderBy指定排序的列名
                // limit指定分页参数
                // distinct可以指定“true”或“false”表示要不要过滤重复值

                // 所有方法将返回一个Cursor对象,代表数据集的游标

                Cursor cursor = sqliteDatabase4.query("user", new String[] {
                        "name" ,"age","path","id"}, null/*"id=?"*/,null /*new String[] { "1" }*/, null, null, null);
                String name = null;
                int age ;
                int id ;
                String path = null;
                //将光标移动到下一行,从而判断该结果集是否还有下一条数据
                //如果有则返回true,没有则返回false
              /*  //Cursor对象常用方法如下:
                c.move(int offset); //以当前位置为参考,移动到指定行
                c.moveToFirst();    //移动到第一行
                c.moveToLast();     //移动到最后一行
                c.moveToPosition(int position); //移动到指定行
                c.moveToPrevious(); //移动到前一行
                c.moveToNext();     //移动到下一行
                c.isFirst();        //是否指向第一条
                c.isLast();     //是否指向最后一条
                c.isBeforeFirst();  //是否指向第一条之前
                c.isAfterLast();    //是否指向最后一条之后
                c.isNull(int columnIndex);  //指定列是否为空(列基数为0)
                c.isClosed();       //游标是否已关闭
                c.getCount();       //总数据项数
                c.getPosition();    //返回当前游标所指向的行数
                c.getColumnIndex(String columnName);//返回某列名对应的列索引值
                c.getString(int columnIndex);   //返回当前行指定列的值 */

                while (cursor.moveToNext()) {
                     id = cursor.getInt(cursor.getColumnIndex("id"));
                     age = cursor.getInt(cursor.getColumnIndex("age"));
                     name = cursor.getString(cursor.getColumnIndex("name"));
                      path = cursor.getString(cursor.getColumnIndex("path"));
                    //输出查询结果
                   Log.e("MainActivity","查询到的数据是:  "+"name: "+name+"age: "+age+"  "+"path: "+path);

                }
                //关闭数据库
                sqliteDatabase4.close();

                break;


            //点击修改数据
            case R.id.modify:
                Toast.makeText(this,"修改数据",Toast.LENGTH_SHORT).show();
                // 创建一个DatabaseHelper对象
                // 将数据库的版本升级为2
                // 传入版本号为2,大于旧版本(1),所以会调用onUpgrade()升级数据库
                MySQLiteOpenHelper dbHelper2 = new MySQLiteOpenHelper(MainActivity.this,"test_db", 1);
                // 调用getWritableDatabase()得到一个可写的SQLiteDatabase对象
                SQLiteDatabase sqliteDatabase2 = dbHelper2.getWritableDatabase();

                // 创建一个ContentValues对象
                ContentValues values2 = new ContentValues();
                values2.put("name", "王五");

                // 调用update方法修改数据库  //更新条件 修改表user 中 id=1 这一行中的name数据修改成zhangsan
                sqliteDatabase2.update("user", values2, "id=?", new String[]{"1"});

                //关闭数据库
                sqliteDatabase2.close();
                break;

            //点击删除数据
            case R.id.delete:
                Toast.makeText(this,"删除数据",Toast.LENGTH_SHORT).show();
                // 创建DatabaseHelper对象
                MySQLiteOpenHelper dbHelper3 = new MySQLiteOpenHelper(MainActivity.this,"test_db",1);

                // 调用getWritableDatabase()方法创建或打开一个可以读的数据库
                SQLiteDatabase sqliteDatabase3 = dbHelper3.getWritableDatabase();

                //删除数据
                sqliteDatabase3.delete("user", "id=?", new String[]{"1"});
                //关闭数据库
                sqliteDatabase3.close();
                break;




            //点击删除数据库
            case R.id.delete_database:
                Toast.makeText(this,"删除数据库",Toast.LENGTH_SHORT).show();
                //删除名为test.db数据库
                deleteDatabase("test_db");
                break;

            default:
                break;

        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值